The data contained in this repository can be downloaded to your computer using one of several clients.
Please see the documentation of your version control software client for more information.

Please select the desired protocol below to get the URL.

This URL has Read-Only access.

Statistics
| Branch: | Revision:

main_repo / deps / v8 / test / cctest / test-log-stack-tracer.cc @ f230a1cf

History | View | Annotate | Download (14 KB)

1
// Copyright 2011 the V8 project authors. All rights reserved.
2
// Redistribution and use in source and binary forms, with or without
3
// modification, are permitted provided that the following conditions are
4
// met:
5
//
6
//     * Redistributions of source code must retain the above copyright
7
//       notice, this list of conditions and the following disclaimer.
8
//     * Redistributions in binary form must reproduce the above
9
//       copyright notice, this list of conditions and the following
10
//       disclaimer in the documentation and/or other materials provided
11
//       with the distribution.
12
//     * Neither the name of Google Inc. nor the names of its
13
//       contributors may be used to endorse or promote products derived
14
//       from this software without specific prior written permission.
15
//
16
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
//
28
// Tests of profiler-related functions from log.h
29

    
30
#include <stdlib.h>
31

    
32
#include "v8.h"
33

    
34
#include "api.h"
35
#include "cctest.h"
36
#include "codegen.h"
37
#include "disassembler.h"
38
#include "isolate.h"
39
#include "log.h"
40
#include "sampler.h"
41
#include "vm-state-inl.h"
42

    
43
using v8::Function;
44
using v8::Local;
45
using v8::Object;
46
using v8::Script;
47
using v8::String;
48
using v8::Value;
49

    
50
using v8::internal::byte;
51
using v8::internal::Address;
52
using v8::internal::Handle;
53
using v8::internal::Isolate;
54
using v8::internal::JSFunction;
55
using v8::internal::RegisterState;
56
using v8::internal::TickSample;
57

    
58

    
59
static struct {
60
  TickSample* sample;
61
} trace_env = { NULL };
62

    
63

    
64
static void InitTraceEnv(TickSample* sample) {
65
  trace_env.sample = sample;
66
}
67

    
68

    
69
static void DoTrace(Address fp) {
70
  RegisterState regs;
71
  regs.fp = fp;
72
  // sp is only used to define stack high bound
73
  regs.sp =
74
      reinterpret_cast<Address>(trace_env.sample) - 10240;
75
  trace_env.sample->Init(CcTest::i_isolate(), regs);
76
}
77

    
78

    
79
// Hide c_entry_fp to emulate situation when sampling is done while
80
// pure JS code is being executed
81
static void DoTraceHideCEntryFPAddress(Address fp) {
82
  v8::internal::Address saved_c_frame_fp =
83
      *(CcTest::i_isolate()->c_entry_fp_address());
84
  CHECK(saved_c_frame_fp);
85
  *(CcTest::i_isolate()->c_entry_fp_address()) = 0;
86
  DoTrace(fp);
87
  *(CcTest::i_isolate()->c_entry_fp_address()) = saved_c_frame_fp;
88
}
89

    
90

    
91
// --- T r a c e   E x t e n s i o n ---
92

    
93
class TraceExtension : public v8::Extension {
94
 public:
95
  TraceExtension() : v8::Extension("v8/trace", kSource) { }
96
  virtual v8::Handle<v8::FunctionTemplate> GetNativeFunction(
97
      v8::Handle<String> name);
98
  static void Trace(const v8::FunctionCallbackInfo<v8::Value>& args);
99
  static void JSTrace(const v8::FunctionCallbackInfo<v8::Value>& args);
100
  static void JSEntrySP(const v8::FunctionCallbackInfo<v8::Value>& args);
101
  static void JSEntrySPLevel2(const v8::FunctionCallbackInfo<v8::Value>& args);
102
 private:
103
  static Address GetFP(const v8::FunctionCallbackInfo<v8::Value>& args);
104
  static const char* kSource;
105
};
106

    
107

    
108
const char* TraceExtension::kSource =
109
    "native function trace();"
110
    "native function js_trace();"
111
    "native function js_entry_sp();"
112
    "native function js_entry_sp_level2();";
113

    
114
v8::Handle<v8::FunctionTemplate> TraceExtension::GetNativeFunction(
115
    v8::Handle<String> name) {
116
  if (name->Equals(String::New("trace"))) {
117
    return v8::FunctionTemplate::New(TraceExtension::Trace);
118
  } else if (name->Equals(String::New("js_trace"))) {
119
    return v8::FunctionTemplate::New(TraceExtension::JSTrace);
120
  } else if (name->Equals(String::New("js_entry_sp"))) {
121
    return v8::FunctionTemplate::New(TraceExtension::JSEntrySP);
122
  } else if (name->Equals(String::New("js_entry_sp_level2"))) {
123
    return v8::FunctionTemplate::New(TraceExtension::JSEntrySPLevel2);
124
  } else {
125
    CHECK(false);
126
    return v8::Handle<v8::FunctionTemplate>();
127
  }
128
}
129

    
130

    
131
Address TraceExtension::GetFP(const v8::FunctionCallbackInfo<v8::Value>& args) {
132
  // Convert frame pointer from encoding as smis in the arguments to a pointer.
133
  CHECK_EQ(2, args.Length());  // Ignore second argument on 32-bit platform.
134
#if defined(V8_HOST_ARCH_32_BIT)
135
  Address fp = *reinterpret_cast<Address*>(*args[0]);
136
#elif defined(V8_HOST_ARCH_64_BIT)
137
  int64_t low_bits = *reinterpret_cast<uint64_t*>(*args[0]) >> 32;
138
  int64_t high_bits = *reinterpret_cast<uint64_t*>(*args[1]);
139
  Address fp = reinterpret_cast<Address>(high_bits | low_bits);
140
#else
141
#error Host architecture is neither 32-bit nor 64-bit.
142
#endif
143
  printf("Trace: %p\n", fp);
144
  return fp;
145
}
146

    
147

    
148
void TraceExtension::Trace(const v8::FunctionCallbackInfo<v8::Value>& args) {
149
  DoTrace(GetFP(args));
150
}
151

    
152

    
153
void TraceExtension::JSTrace(const v8::FunctionCallbackInfo<v8::Value>& args) {
154
  DoTraceHideCEntryFPAddress(GetFP(args));
155
}
156

    
157

    
158
static Address GetJsEntrySp() {
159
  CHECK_NE(NULL, CcTest::i_isolate()->thread_local_top());
160
  return CcTest::i_isolate()->js_entry_sp();
161
}
162

    
163

    
164
void TraceExtension::JSEntrySP(
165
    const v8::FunctionCallbackInfo<v8::Value>& args) {
166
  CHECK_NE(0, GetJsEntrySp());
167
}
168

    
169

    
170
void TraceExtension::JSEntrySPLevel2(
171
    const v8::FunctionCallbackInfo<v8::Value>& args) {
172
  v8::HandleScope scope(args.GetIsolate());
173
  const Address js_entry_sp = GetJsEntrySp();
174
  CHECK_NE(0, js_entry_sp);
175
  CompileRun("js_entry_sp();");
176
  CHECK_EQ(js_entry_sp, GetJsEntrySp());
177
}
178

    
179

    
180
static TraceExtension kTraceExtension;
181
v8::DeclareExtension kTraceExtensionDeclaration(&kTraceExtension);
182

    
183

    
184
static bool IsAddressWithinFuncCode(JSFunction* function, Address addr) {
185
  i::Code* code = function->code();
186
  return code->contains(addr);
187
}
188

    
189

    
190
static bool IsAddressWithinFuncCode(v8::Local<v8::Context> context,
191
                                    const char* func_name,
192
                                    Address addr) {
193
  v8::Local<v8::Value> func = context->Global()->Get(v8_str(func_name));
194
  CHECK(func->IsFunction());
195
  JSFunction* js_func = JSFunction::cast(*v8::Utils::OpenHandle(*func));
196
  return IsAddressWithinFuncCode(js_func, addr);
197
}
198

    
199

    
200
// This C++ function is called as a constructor, to grab the frame pointer
201
// from the calling function.  When this function runs, the stack contains
202
// a C_Entry frame and a Construct frame above the calling function's frame.
203
static void construct_call(const v8::FunctionCallbackInfo<v8::Value>& args) {
204
  i::Isolate* isolate = reinterpret_cast<i::Isolate*>(args.GetIsolate());
205
  i::StackFrameIterator frame_iterator(isolate);
206
  CHECK(frame_iterator.frame()->is_exit());
207
  frame_iterator.Advance();
208
  CHECK(frame_iterator.frame()->is_construct());
209
  frame_iterator.Advance();
210
  i::StackFrame* calling_frame = frame_iterator.frame();
211
  CHECK(calling_frame->is_java_script());
212

    
213
#if defined(V8_HOST_ARCH_32_BIT)
214
  int32_t low_bits = reinterpret_cast<int32_t>(calling_frame->fp());
215
  args.This()->Set(v8_str("low_bits"), v8_num(low_bits >> 1));
216
#elif defined(V8_HOST_ARCH_64_BIT)
217
  uint64_t fp = reinterpret_cast<uint64_t>(calling_frame->fp());
218
  int32_t low_bits = static_cast<int32_t>(fp & 0xffffffff);
219
  int32_t high_bits = static_cast<int32_t>(fp >> 32);
220
  args.This()->Set(v8_str("low_bits"), v8_num(low_bits));
221
  args.This()->Set(v8_str("high_bits"), v8_num(high_bits));
222
#else
223
#error Host architecture is neither 32-bit nor 64-bit.
224
#endif
225
  args.GetReturnValue().Set(args.This());
226
}
227

    
228

    
229
// Use the API to create a JSFunction object that calls the above C++ function.
230
void CreateFramePointerGrabberConstructor(v8::Local<v8::Context> context,
231
                                          const char* constructor_name) {
232
    Local<v8::FunctionTemplate> constructor_template =
233
        v8::FunctionTemplate::New(construct_call);
234
    constructor_template->SetClassName(v8_str("FPGrabber"));
235
    Local<Function> fun = constructor_template->GetFunction();
236
    context->Global()->Set(v8_str(constructor_name), fun);
237
}
238

    
239

    
240
// Creates a global function named 'func_name' that calls the tracing
241
// function 'trace_func_name' with an actual EBP register value,
242
// encoded as one or two Smis.
243
static void CreateTraceCallerFunction(v8::Local<v8::Context> context,
244
                                      const char* func_name,
245
                                      const char* trace_func_name) {
246
  i::EmbeddedVector<char, 256> trace_call_buf;
247
  i::OS::SNPrintF(trace_call_buf,
248
                  "function %s() {"
249
                  "  fp = new FPGrabber();"
250
                  "  %s(fp.low_bits, fp.high_bits);"
251
                  "}",
252
                  func_name, trace_func_name);
253

    
254
  // Create the FPGrabber function, which grabs the caller's frame pointer
255
  // when called as a constructor.
256
  CreateFramePointerGrabberConstructor(context, "FPGrabber");
257

    
258
  // Compile the script.
259
  CompileRun(trace_call_buf.start());
260
}
261

    
262

    
263
// This test verifies that stack tracing works when called during
264
// execution of a native function called from JS code. In this case,
265
// TickSample::Trace uses Isolate::c_entry_fp as a starting point for stack
266
// walking.
267
TEST(CFromJSStackTrace) {
268
  // BUG(1303) Inlining of JSFuncDoTrace() in JSTrace below breaks this test.
269
  i::FLAG_use_inlining = false;
270

    
271
  TickSample sample;
272
  InitTraceEnv(&sample);
273

    
274
  v8::HandleScope scope(CcTest::isolate());
275
  v8::Local<v8::Context> context = CcTest::NewContext(TRACE_EXTENSION);
276
  v8::Context::Scope context_scope(context);
277

    
278
  // Create global function JSFuncDoTrace which calls
279
  // extension function trace() with the current frame pointer value.
280
  CreateTraceCallerFunction(context, "JSFuncDoTrace", "trace");
281
  Local<Value> result = CompileRun(
282
      "function JSTrace() {"
283
      "         JSFuncDoTrace();"
284
      "};\n"
285
      "JSTrace();\n"
286
      "true;");
287
  CHECK(!result.IsEmpty());
288
  // When stack tracer is invoked, the stack should look as follows:
289
  // script [JS]
290
  //   JSTrace() [JS]
291
  //     JSFuncDoTrace() [JS] [captures EBP value and encodes it as Smi]
292
  //       trace(EBP) [native (extension)]
293
  //         DoTrace(EBP) [native]
294
  //           TickSample::Trace
295

    
296
  CHECK(sample.has_external_callback);
297
  CHECK_EQ(FUNCTION_ADDR(TraceExtension::Trace), sample.external_callback);
298

    
299
  // Stack tracing will start from the first JS function, i.e. "JSFuncDoTrace"
300
  int base = 0;
301
  CHECK_GT(sample.frames_count, base + 1);
302

    
303
  CHECK(IsAddressWithinFuncCode(
304
      context, "JSFuncDoTrace", sample.stack[base + 0]));
305
  CHECK(IsAddressWithinFuncCode(context, "JSTrace", sample.stack[base + 1]));
306
}
307

    
308

    
309
// This test verifies that stack tracing works when called during
310
// execution of JS code. However, as calling TickSample::Trace requires
311
// entering native code, we can only emulate pure JS by erasing
312
// Isolate::c_entry_fp value. In this case, TickSample::Trace uses passed frame
313
// pointer value as a starting point for stack walking.
314
TEST(PureJSStackTrace) {
315
  // This test does not pass with inlining enabled since inlined functions
316
  // don't appear in the stack trace.
317
  i::FLAG_use_inlining = false;
318

    
319
  TickSample sample;
320
  InitTraceEnv(&sample);
321

    
322
  v8::HandleScope scope(CcTest::isolate());
323
  v8::Local<v8::Context> context = CcTest::NewContext(TRACE_EXTENSION);
324
  v8::Context::Scope context_scope(context);
325

    
326
  // Create global function JSFuncDoTrace which calls
327
  // extension function js_trace() with the current frame pointer value.
328
  CreateTraceCallerFunction(context, "JSFuncDoTrace", "js_trace");
329
  Local<Value> result = CompileRun(
330
      "function JSTrace() {"
331
      "         JSFuncDoTrace();"
332
      "};\n"
333
      "function OuterJSTrace() {"
334
      "         JSTrace();"
335
      "};\n"
336
      "OuterJSTrace();\n"
337
      "true;");
338
  CHECK(!result.IsEmpty());
339
  // When stack tracer is invoked, the stack should look as follows:
340
  // script [JS]
341
  //   OuterJSTrace() [JS]
342
  //     JSTrace() [JS]
343
  //       JSFuncDoTrace() [JS]
344
  //         js_trace(EBP) [native (extension)]
345
  //           DoTraceHideCEntryFPAddress(EBP) [native]
346
  //             TickSample::Trace
347
  //
348

    
349
  CHECK(sample.has_external_callback);
350
  CHECK_EQ(FUNCTION_ADDR(TraceExtension::JSTrace), sample.external_callback);
351

    
352
  // Stack sampling will start from the caller of JSFuncDoTrace, i.e. "JSTrace"
353
  int base = 0;
354
  CHECK_GT(sample.frames_count, base + 1);
355
  CHECK(IsAddressWithinFuncCode(context, "JSTrace", sample.stack[base + 0]));
356
  CHECK(IsAddressWithinFuncCode(
357
      context, "OuterJSTrace", sample.stack[base + 1]));
358
}
359

    
360

    
361
static void CFuncDoTrace(byte dummy_parameter) {
362
  Address fp;
363
#ifdef __GNUC__
364
  fp = reinterpret_cast<Address>(__builtin_frame_address(0));
365
#elif defined _MSC_VER
366
  // Approximate a frame pointer address. We compile without base pointers,
367
  // so we can't trust ebp/rbp.
368
  fp = &dummy_parameter - 2 * sizeof(void*);  // NOLINT
369
#else
370
#error Unexpected platform.
371
#endif
372
  DoTrace(fp);
373
}
374

    
375

    
376
static int CFunc(int depth) {
377
  if (depth <= 0) {
378
    CFuncDoTrace(0);
379
    return 0;
380
  } else {
381
    return CFunc(depth - 1) + 1;
382
  }
383
}
384

    
385

    
386
// This test verifies that stack tracing doesn't crash when called on
387
// pure native code. TickSample::Trace only unrolls JS code, so we can't
388
// get any meaningful info here.
389
TEST(PureCStackTrace) {
390
  TickSample sample;
391
  InitTraceEnv(&sample);
392
  v8::HandleScope scope(CcTest::isolate());
393
  v8::Local<v8::Context> context = CcTest::NewContext(TRACE_EXTENSION);
394
  v8::Context::Scope context_scope(context);
395
  // Check that sampler doesn't crash
396
  CHECK_EQ(10, CFunc(10));
397
}
398

    
399

    
400
TEST(JsEntrySp) {
401
  v8::HandleScope scope(CcTest::isolate());
402
  v8::Local<v8::Context> context = CcTest::NewContext(TRACE_EXTENSION);
403
  v8::Context::Scope context_scope(context);
404
  CHECK_EQ(0, GetJsEntrySp());
405
  CompileRun("a = 1; b = a + 1;");
406
  CHECK_EQ(0, GetJsEntrySp());
407
  CompileRun("js_entry_sp();");
408
  CHECK_EQ(0, GetJsEntrySp());
409
  CompileRun("js_entry_sp_level2();");
410
  CHECK_EQ(0, GetJsEntrySp());
411
}