Repository navigation
Expand file tree
/
Copy pathcmd_where.cpp
More file actions
252 lines (234 loc) · 8.72 KB
/
Copy pathcmd_where.cpp
File metadata and controls
252 lines (234 loc) · 8.72 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
/*
+----------------------------------------------------------------------+
| HipHop for PHP |
+----------------------------------------------------------------------+
| Copyright (c) 2010-2013 Facebook, Inc. (http://www.facebook.com) |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
*/
#include "hphp/runtime/debugger/cmd/cmd_where.h"
#include "hphp/runtime/base/array-iterator.h"
#include "hphp/runtime/base/comparisons.h"
#include "hphp/runtime/ext/ext_asio.h"
#include "hphp/runtime/ext/ext_continuation.h"
#include "hphp/runtime/ext/asio/async_function_wait_handle.h"
#include "hphp/runtime/ext/asio/waitable_wait_handle.h"
namespace HPHP { namespace Eval {
///////////////////////////////////////////////////////////////////////////////
TRACE_SET_MOD(debugger);
void CmdWhere::sendImpl(DebuggerThriftBuffer &thrift) {
DebuggerCommand::sendImpl(thrift);
{
String sdata;
DebuggerWireHelpers::WireSerialize(m_stacktrace, sdata);
thrift.write(sdata);
}
thrift.write(m_stackArgs);
}
void CmdWhere::recvImpl(DebuggerThriftBuffer &thrift) {
DebuggerCommand::recvImpl(thrift);
{
String sdata;
thrift.read(sdata);
if (DebuggerWireHelpers::WireUnserialize(sdata, m_stacktrace) !=
DebuggerWireHelpers::NoError) {
m_stacktrace = null_array;
m_wireError = sdata;
}
}
thrift.read(m_stackArgs);
}
void CmdWhere::help(DebuggerClient &client) {
client.helpTitle("Where Command");
client.helpCmds(
"[w]here", "displays current stacktrace",
"[w]here [a]sync", "displays the current async stacktrace",
"wa", "shortcut for [w]here [a]sync",
"[w]here {[a]sync]} {num}", "displays number of innermost frames",
"[w]here {[a]sync]} -{num}", "displays number of outermost frames",
nullptr
);
client.helpBody(
"Use '[u]p {num}' or '[d]own {num}' to walk up or down the stacktrace. "
"Use '[f]rame {index}' to jump to one particular frame. At any frame, "
"Use '[v]ariable' command to display all local variables.\n"
"\n"
"Use '[w]here [a]sync' from within an async method, like a generator, "
"to get the current stack of async methods."
);
}
Array CmdWhere::fetchStackTrace(DebuggerClient &client) {
Array st = client.getStackTrace();
// Only grab a new stack trace if we don't have one cached, or if
// the one cached does not match the type of stack trace being
// requested.
bool isAsync = m_type == KindOfWhereAsync;
if (st.isNull() || (isAsync != client.isStackTraceAsync())) {
m_stackArgs = client.getDebuggerClientStackArgs();
CmdWherePtr cmd = client.xend<CmdWhere>(this);
st = cmd->m_stacktrace;
client.setStackTrace(st, isAsync);
}
return st;
}
void CmdWhere::onClient(DebuggerClient &client) {
if (DebuggerCommand::displayedHelp(client)) return;
if (client.argCount() > 2) {
help(client);
return;
}
int argBase = 1;
if ((client.argCount() > 0) && client.arg(argBase, "async")) {
// We use a different command type for an async stack trace, so we
// can both send and receive different data and still keep the
// existing Where command unchanged. This ensures that old clients
// can still get a stack trace from a newer server, and vice
// versa.
m_type = KindOfWhereAsync;
argBase++;
client.info("Fetching async stacktrace...");
}
Array st = fetchStackTrace(client);
if (st.empty()) {
if (m_type != KindOfWhereAsync) {
client.info("(no stacktrace to display or in global scope)");
client.info("If you hit the serialization limit, try "
"\"set sa off\" to get the stack without args");
} else {
client.info("(no async stacktrace to display)");
}
return;
}
// so list command can default to current frame
client.moveToFrame(client.getFrame(), false);
if (client.argCount() < argBase) {
int i = 0;
for (ArrayIter iter(st); iter; ++iter) {
client.printFrame(i, iter.second().toArray());
++i;
if (i % DebuggerClient::ScrollBlockSize == 0 &&
client.ask("There are %zd more frames. Continue? [Y/n]",
st.size() - i) == 'n') {
break;
}
}
} else {
string snum = client.argValue(argBase);
int num = atoi(snum.c_str());
if (snum[0] == '-') {
snum = snum.substr(1);
}
if (!DebuggerClient::IsValidNumber(snum)) {
client.error("The argument, if specified, has to be numeric.");
return;
}
if (num > 0) {
for (int i = 0; i < num && i < st.size(); i++) {
client.printFrame(i, st[i].toArray());
}
} else if (num < 0) {
for (int i = st.size() + num; i < st.size(); i++) {
client.printFrame(i, st[i].toArray());
}
} else {
client.error("0 was specified for the number of frames");
client.tutorial(
"The optional argument is the number of frames to print out. "
"Use a positive number to print out innermost frames. Use a negative "
"number to print out outermost frames."
);
}
}
}
void CmdWhere::removeArgs() {
// Strip out the args from the stack
static StaticString s_args("args");
Array smallST;
for (ArrayIter iter(m_stacktrace); iter; ++iter) {
CArrRef frame(iter.secondRef().toArray());
Array smallFrame;
for (ArrayIter iter2(frame); iter2; ++iter2) {
if (equal(iter2.first(), s_args)) {
continue;
}
smallFrame.set(iter2.first(), iter2.secondRef());
}
smallST.append(smallFrame);
}
m_stacktrace = smallST;
}
c_WaitableWaitHandle *objToWaitableWaitHandle(Object o) {
assert(o->instanceof(c_WaitableWaitHandle::classof()));
return static_cast<c_WaitableWaitHandle*>(o.get());
}
const StaticString
s_function("function"),
s_id("id"),
s_file("file"),
s_line("line"),
s_ancestors("ancestors");
// Add location information for the given continuation to the given frame.
void addContinuationLocation(Array& frameData,
c_AsyncFunctionWaitHandle& contWh) {
// A running continuation is active on the normal stack, and we
// cannot compute the location just by inspecting the continuation
// alone.
if (contWh.isRunning()) return;
frameData.set(s_file, contWh.getFileName(), true);
frameData.set(s_line, contWh.getLineNumber(), true);
}
// Form a trace of the async stack starting with the currently running
// generator, if any. For now we just toss in the function name and
// id, as well as the pseudo-frames for context breaks at explicit
// joins. Later we'll add more, like file and line, hopefully function
// args, wait handle status, etc.
static Array createAsyncStacktrace() {
Array trace;
auto currentWaitHandle = f_asio_get_running();
if (currentWaitHandle.isNull()) return trace;
Array depStack =
objToWaitableWaitHandle(currentWaitHandle)->t_getdependencystack();
for (ArrayIter iter(depStack); iter; ++iter) {
if (iter.secondRef().isNull()) {
trace.append(ArrayInit(0).toVariant());
} else {
auto wh = objToWaitableWaitHandle(iter.secondRef().toObject());
auto parents = wh->t_getparents();
Array ancestors;
for (ArrayIter piter(parents); piter; ++piter) {
// Note: the parent list contains no nulls.
auto parent = objToWaitableWaitHandle(piter.secondRef().toObject());
ancestors.append(parent->t_getname());
}
Array frameData;
frameData.set(s_function, wh->t_getname(), true);
frameData.set(s_id, wh->t_getid(), true);
frameData.set(s_ancestors, ancestors, true);
// Continuation wait handles may have a source location to add.
auto contWh = dynamic_cast<c_AsyncFunctionWaitHandle*>(wh);
if (contWh != nullptr) addContinuationLocation(frameData, *contWh);
trace.append(frameData);
}
}
return trace;
}
bool CmdWhere::onServer(DebuggerProxy &proxy) {
if (m_type == KindOfWhereAsync) {
m_stacktrace = createAsyncStacktrace();
} else {
m_stacktrace = g_vmContext->debugBacktrace(false, true, false);
if (!m_stackArgs) {
removeArgs();
}
}
return proxy.sendToClient(this);
}
///////////////////////////////////////////////////////////////////////////////
}}