add an example to showcase a bug in closure generation
[hiphop-php.git] / hphp / runtime / ext / vsdebug / stack_trace_command.cpp
blobf21ffece63e6e6a55c8c96cf59b1e1cf0531bb6f
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2017-present Facebook, Inc. (http://www.facebook.com) |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
17 #include "hphp/runtime/ext/vsdebug/debugger.h"
18 #include "hphp/runtime/ext/vsdebug/debugger-request-info.h"
19 #include "hphp/runtime/ext/vsdebug/command.h"
20 #include "hphp/runtime/base/backtrace.h"
21 #include "hphp/runtime/base/tv-variant.h"
23 #include <filesystem>
25 namespace HPHP {
26 namespace VSDEBUG {
28 StackTraceCommand::StackTraceCommand(
29 Debugger* debugger,
30 folly::dynamic message
31 ) : VSCommand(debugger, message) {
34 StackTraceCommand::~StackTraceCommand() {
37 const StaticString s_file("file");
38 const StaticString s_line("line");
39 const StaticString s_function("function");
41 bool StackTraceCommand::executeImpl(
42 DebuggerSession* session,
43 folly::dynamic* responseMsg
44 ) {
45 // The request thread should not re-enter the debugger while
46 // processing this command.
47 DebuggerNoBreakContext noBreak(m_debugger);
49 const request_id_t requestId = m_debugger->getCurrentThreadId();
50 const folly::dynamic& message = getMessage();
51 const folly::dynamic& args = tryGetObject(message, "arguments", s_emptyArgs);
53 (*responseMsg)["body"] = folly::dynamic::object;
55 folly::dynamic& stackTrace = (*responseMsg)["body"];
56 stackTrace["stackFrames"] = folly::dynamic::array();
57 folly::dynamic& frames = stackTrace["stackFrames"];
59 if (m_debugger->getRequestInfo()->m_pauseRecurseCount == 0) {
60 stackTrace["stackFrames"] = frames;
61 stackTrace["totalFrames"] = 0;
62 (*responseMsg)["body"] = stackTrace;
63 return false;
66 int startFrame = tryGetInt(args, "startFrame", 0);
67 int levels = tryGetInt(args, "levels", INT_MAX);
68 if (levels == 0) {
69 // Per protocol: if levels is 0, all frames should be returned.
70 levels = INT_MAX;
72 int levelsAdded = 0;
74 // Respond with a stack trace!
75 auto backtraceArgs = BacktraceArgs()
76 .withSelf()
77 .setParserFrame(nullptr);
79 const Array backtrace = createBacktrace(backtraceArgs);
80 int backtraceSize = backtrace.size();
81 const ClientPreferences& prefs = m_debugger->getClientPreferences();
83 for (int depth = 0; depth < backtraceSize - 1; depth++) {
84 if (depth < startFrame) {
85 continue;
88 if (levelsAdded >= levels) {
89 break;
92 auto const parentFrame = backtrace.lookup(depth + 1);
93 auto const funcName =
94 tvCastToString(parentFrame.val().parr->get(s_function)).data();
95 auto const frame = backtrace.lookup(depth);
96 const auto file = frame.val().parr->get(s_file);
97 const auto line = frame.val().parr->get(s_line);
99 frames.push_back(folly::dynamic::object);
100 folly::dynamic& stackFrame = frames[frames.size() - 1];
101 stackFrame["id"] = session->generateFrameId(requestId, depth);
102 stackFrame["name"] = funcName;
104 int64_t lineNumber = tvCastToInt64(line);
105 if (!prefs.linesStartAt1) {
106 lineNumber--;
109 stackFrame["line"] = lineNumber;
110 stackFrame["column"] = prefs.columnsStartAt1 ? 1 : 0;
112 std::string fileName = tvCastToString(file).toCppString();
113 if (!fileName.empty()) {
114 stackFrame["source"] = folly::dynamic::object;
115 folly::dynamic& source = stackFrame["source"];
116 source["name"] = std::filesystem::path{fileName}.stem().native();
117 source["path"] = fileName;
120 levelsAdded++;
123 if (backtrace.size() == 0) {
124 // The backtrace will be empty if the request is just starting up and hasn't
125 // invoked anything yet.
126 frames.push_back(folly::dynamic::object);
127 folly::dynamic& stackFrame = frames[frames.size() - 1];
128 stackFrame["id"] = -1;
129 stackFrame["name"] = "{request initializing}";
130 stackFrame["line"] = 0;
131 stackFrame["column"] = 0;
133 folly::dynamic source = folly::dynamic::object;
134 source["name"] = "<unknown>";
135 source["path"] = "<unknown>";
136 stackFrame["source"] = source;
139 stackTrace["totalFrames"] = backtraceSize == 0 ? 0 : backtraceSize - 1;
141 // Completion of this command does not resume the target.
142 return false;