Fix handling of stackTrace request with levels == 0
[hiphop-php.git] / hphp / runtime / ext / vsdebug / stack_trace_command.cpp
blob49f5e992d15f422d56fa480445a75363c3833147
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/command.h"
19 #include "hphp/runtime/base/backtrace.h"
20 #include "hphp/runtime/base/tv-variant.h"
22 namespace HPHP {
23 namespace VSDEBUG {
25 StackTraceCommand::StackTraceCommand(
26 Debugger* debugger,
27 folly::dynamic message
28 ) : VSCommand(debugger, message) {
31 StackTraceCommand::~StackTraceCommand() {
34 const StaticString s_file("file");
35 const StaticString s_line("line");
36 const StaticString s_function("function");
38 bool StackTraceCommand::executeImpl(
39 DebuggerSession* session,
40 folly::dynamic* responseMsg
41 ) {
42 const request_id_t requestId = m_debugger->getCurrentThreadId();
43 const folly::dynamic& message = getMessage();
44 const folly::dynamic& args = tryGetObject(message, "arguments", s_emptyArgs);
46 (*responseMsg)["body"] = folly::dynamic::object;
48 folly::dynamic& stackTrace = (*responseMsg)["body"];
49 stackTrace["stackFrames"] = folly::dynamic::array();
50 folly::dynamic& frames = stackTrace["stackFrames"];
52 if (m_debugger->getRequestInfo()->m_pauseRecurseCount == 0) {
53 stackTrace["stackFrames"] = frames;
54 stackTrace["totalFrames"] = 0;
55 (*responseMsg)["body"] = stackTrace;
56 return false;
59 int startFrame = tryGetInt(args, "startFrame", 0);
60 int levels = tryGetInt(args, "levels", INT_MAX);
61 if (levels == 0) {
62 // Per protocol: if levels is 0, all frames should be returned.
63 levels = INT_MAX;
65 int levelsAdded = 0;
67 // Respond with a stack trace!
68 auto backtraceArgs = BacktraceArgs()
69 .withSelf()
70 .withPseudoMain()
71 .setParserFrame(nullptr);
73 const Array backtrace = createBacktrace(backtraceArgs);
74 int backtraceSize = backtrace.size();
75 const ClientPreferences& prefs = m_debugger->getClientPreferences();
77 for (int depth = 0; depth < backtraceSize - 1; depth++) {
78 if (depth < startFrame) {
79 continue;
82 if (levelsAdded >= levels) {
83 break;
86 auto const parentFrame = backtrace.rvalAt(depth + 1).unboxed();
87 auto const funcName =
88 tvCastToString(parentFrame.val().parr->get(s_function).tv()).data();
89 auto const frame = backtrace.rvalAt(depth).unboxed();
90 const auto file = frame.val().parr->get(s_file);
91 const auto line = frame.val().parr->get(s_line);
93 frames.push_back(folly::dynamic::object);
94 folly::dynamic& stackFrame = frames[frames.size() - 1];
95 stackFrame["id"] = session->generateFrameId(requestId, depth);
96 stackFrame["name"] = funcName;
98 int64_t lineNumber = tvCastToInt64(line.tv());
99 if (!prefs.linesStartAt1) {
100 lineNumber--;
103 stackFrame["line"] = lineNumber;
104 stackFrame["column"] = 0;
105 stackFrame["source"] = folly::dynamic::object;
107 folly::dynamic& source = stackFrame["source"];
108 std::string fileName = tvCastToString(file.tv()).toCppString();
110 if (fileName.empty()) {
111 // Some routines like builtins and native extensions do not have
112 // a PHP file path in their frame's file name field.
113 fileName = std::string("<unknown>");
116 source["name"] = fileName;
117 source["path"] = fileName;
119 levelsAdded++;
122 if (backtrace.size() == 0) {
123 // The backtrace will be empty if the request is just starting up and hasn't
124 // invoked anything yet.
125 frames.push_back(folly::dynamic::object);
126 folly::dynamic& stackFrame = frames[frames.size() - 1];
127 stackFrame["id"] = -1;
128 stackFrame["name"] = "{request initializing}";
129 stackFrame["line"] = 0;
130 stackFrame["column"] = 0;
132 folly::dynamic source = folly::dynamic::object;
133 source["name"] = "<unknown>";
134 source["path"] = "<unknown>";
135 stackFrame["source"] = source;
138 stackTrace["totalFrames"] = backtraceSize == 0 ? 0 : backtraceSize - 1;
140 // Completion of this command does not resume the target.
141 return false;