V8 API Reference, 7.2.502.16 (for Deno 0.2.4)
tick-sample.cc
1 // Copyright 2013 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "src/profiler/tick-sample.h"
6 
7 #include "include/v8-profiler.h"
8 #include "src/counters.h"
9 #include "src/frames-inl.h"
10 #include "src/msan.h"
11 #include "src/simulator.h"
12 #include "src/vm-state-inl.h"
13 
14 namespace v8 {
15 namespace {
16 
17 bool IsSamePage(i::Address ptr1, i::Address ptr2) {
18  const uint32_t kPageSize = 4096;
19  i::Address mask = ~static_cast<i::Address>(kPageSize - 1);
20  return (ptr1 & mask) == (ptr2 & mask);
21 }
22 
23 // Check if the code at specified address could potentially be a
24 // frame setup code.
25 bool IsNoFrameRegion(i::Address address) {
26  struct Pattern {
27  int bytes_count;
28  i::byte bytes[8];
29  int offsets[4];
30  };
31  static Pattern patterns[] = {
32 #if V8_HOST_ARCH_IA32
33  // push %ebp
34  // mov %esp,%ebp
35  {3, {0x55, 0x89, 0xE5}, {0, 1, -1}},
36  // pop %ebp
37  // ret N
38  {2, {0x5D, 0xC2}, {0, 1, -1}},
39  // pop %ebp
40  // ret
41  {2, {0x5D, 0xC3}, {0, 1, -1}},
42 #elif V8_HOST_ARCH_X64
43  // pushq %rbp
44  // movq %rsp,%rbp
45  {4, {0x55, 0x48, 0x89, 0xE5}, {0, 1, -1}},
46  // popq %rbp
47  // ret N
48  {2, {0x5D, 0xC2}, {0, 1, -1}},
49  // popq %rbp
50  // ret
51  {2, {0x5D, 0xC3}, {0, 1, -1}},
52 #endif
53  {0, {}, {}}
54  };
55  i::byte* pc = reinterpret_cast<i::byte*>(address);
56  for (Pattern* pattern = patterns; pattern->bytes_count; ++pattern) {
57  for (int* offset_ptr = pattern->offsets; *offset_ptr != -1; ++offset_ptr) {
58  int offset = *offset_ptr;
59  if (!offset || IsSamePage(address, address - offset)) {
60  MSAN_MEMORY_IS_INITIALIZED(pc - offset, pattern->bytes_count);
61  if (!memcmp(pc - offset, pattern->bytes, pattern->bytes_count))
62  return true;
63  } else {
64  // It is not safe to examine bytes on another page as it might not be
65  // allocated thus causing a SEGFAULT.
66  // Check the pattern part that's on the same page and
67  // pessimistically assume it could be the entire pattern match.
68  MSAN_MEMORY_IS_INITIALIZED(pc, pattern->bytes_count - offset);
69  if (!memcmp(pc, pattern->bytes + offset, pattern->bytes_count - offset))
70  return true;
71  }
72  }
73  }
74  return false;
75 }
76 
77 } // namespace
78 
79 namespace internal {
80 namespace {
81 
82 #if defined(USE_SIMULATOR)
83 class SimulatorHelper {
84  public:
85  // Returns true if register values were successfully retrieved
86  // from the simulator, otherwise returns false.
87  static bool FillRegisters(Isolate* isolate, v8::RegisterState* state);
88 };
89 
90 bool SimulatorHelper::FillRegisters(Isolate* isolate,
91  v8::RegisterState* state) {
92  Simulator* simulator = isolate->thread_local_top()->simulator_;
93  // Check if there is active simulator.
94  if (simulator == nullptr) return false;
95 #if V8_TARGET_ARCH_ARM
96  if (!simulator->has_bad_pc()) {
97  state->pc = reinterpret_cast<void*>(simulator->get_pc());
98  }
99  state->sp = reinterpret_cast<void*>(simulator->get_register(Simulator::sp));
100  state->fp = reinterpret_cast<void*>(simulator->get_register(Simulator::r11));
101 #elif V8_TARGET_ARCH_ARM64
102  state->pc = reinterpret_cast<void*>(simulator->pc());
103  state->sp = reinterpret_cast<void*>(simulator->sp());
104  state->fp = reinterpret_cast<void*>(simulator->fp());
105 #elif V8_TARGET_ARCH_MIPS || V8_TARGET_ARCH_MIPS64
106  if (!simulator->has_bad_pc()) {
107  state->pc = reinterpret_cast<void*>(simulator->get_pc());
108  }
109  state->sp = reinterpret_cast<void*>(simulator->get_register(Simulator::sp));
110  state->fp = reinterpret_cast<void*>(simulator->get_register(Simulator::fp));
111 #elif V8_TARGET_ARCH_PPC
112  if (!simulator->has_bad_pc()) {
113  state->pc = reinterpret_cast<void*>(simulator->get_pc());
114  }
115  state->sp = reinterpret_cast<void*>(simulator->get_register(Simulator::sp));
116  state->fp = reinterpret_cast<void*>(simulator->get_register(Simulator::fp));
117 #elif V8_TARGET_ARCH_S390
118  if (!simulator->has_bad_pc()) {
119  state->pc = reinterpret_cast<void*>(simulator->get_pc());
120  }
121  state->sp = reinterpret_cast<void*>(simulator->get_register(Simulator::sp));
122  state->fp = reinterpret_cast<void*>(simulator->get_register(Simulator::fp));
123 #endif
124  if (state->sp == 0 || state->fp == 0) {
125  // It possible that the simulator is interrupted while it is updating
126  // the sp or fp register. ARM64 simulator does this in two steps:
127  // first setting it to zero and then setting it to the new value.
128  // Bailout if sp/fp doesn't contain the new value.
129  //
130  // FIXME: The above doesn't really solve the issue.
131  // If a 64-bit target is executed on a 32-bit host even the final
132  // write is non-atomic, so it might obtain a half of the result.
133  // Moreover as long as the register set code uses memcpy (as of now),
134  // it is not guaranteed to be atomic even when both host and target
135  // are of same bitness.
136  return false;
137  }
138  return true;
139 }
140 #endif // USE_SIMULATOR
141 
142 } // namespace
143 } // namespace internal
144 
145 //
146 // StackTracer implementation
147 //
148 DISABLE_ASAN void TickSample::Init(Isolate* v8_isolate,
149  const RegisterState& reg_state,
150  RecordCEntryFrame record_c_entry_frame,
151  bool update_stats,
152  bool use_simulator_reg_state) {
153  this->update_stats = update_stats;
154  SampleInfo info;
155  RegisterState regs = reg_state;
156  if (!GetStackSample(v8_isolate, &regs, record_c_entry_frame, stack,
157  kMaxFramesCount, &info, use_simulator_reg_state)) {
158  // It is executing JS but failed to collect a stack trace.
159  // Mark the sample as spoiled.
160  pc = nullptr;
161  return;
162  }
163 
164  state = info.vm_state;
165  pc = regs.pc;
166  frames_count = static_cast<unsigned>(info.frames_count);
167  has_external_callback = info.external_callback_entry != nullptr;
168  if (has_external_callback) {
169  external_callback_entry = info.external_callback_entry;
170  } else if (frames_count) {
171  // sp register may point at an arbitrary place in memory, make
172  // sure MSAN doesn't complain about it.
173  MSAN_MEMORY_IS_INITIALIZED(regs.sp, sizeof(void*));
174  // Sample potential return address value for frameless invocation of
175  // stubs (we'll figure out later, if this value makes sense).
176  tos = reinterpret_cast<void*>(
177  i::Memory<i::Address>(reinterpret_cast<i::Address>(regs.sp)));
178  } else {
179  tos = nullptr;
180  }
181 }
182 
183 bool TickSample::GetStackSample(Isolate* v8_isolate, RegisterState* regs,
184  RecordCEntryFrame record_c_entry_frame,
185  void** frames, size_t frames_limit,
186  v8::SampleInfo* sample_info,
187  bool use_simulator_reg_state) {
188  i::Isolate* isolate = reinterpret_cast<i::Isolate*>(v8_isolate);
189  sample_info->frames_count = 0;
190  sample_info->vm_state = isolate->current_vm_state();
191  sample_info->external_callback_entry = nullptr;
192  if (sample_info->vm_state == GC) return true;
193 
194  i::Address js_entry_sp = isolate->js_entry_sp();
195  if (js_entry_sp == 0) return true; // Not executing JS now.
196 
197 #if defined(USE_SIMULATOR)
198  if (use_simulator_reg_state) {
199  if (!i::SimulatorHelper::FillRegisters(isolate, regs)) return false;
200  }
201 #else
202  USE(use_simulator_reg_state);
203 #endif
204  DCHECK(regs->sp);
205 
206  // Check whether we interrupted setup/teardown of a stack frame in JS code.
207  // Avoid this check for C++ code, as that would trigger false positives.
208  if (regs->pc &&
209  isolate->heap()->memory_allocator()->code_range().contains(
210  reinterpret_cast<i::Address>(regs->pc)) &&
211  IsNoFrameRegion(reinterpret_cast<i::Address>(regs->pc))) {
212  // The frame is not setup, so it'd be hard to iterate the stack. Bailout.
213  return false;
214  }
215 
216  i::ExternalCallbackScope* scope = isolate->external_callback_scope();
217  i::Address handler = i::Isolate::handler(isolate->thread_local_top());
218  // If there is a handler on top of the external callback scope then
219  // we have already entrered JavaScript again and the external callback
220  // is not the top function.
221  if (scope && scope->scope_address() < handler) {
222  i::Address* external_callback_entry_ptr =
223  scope->callback_entrypoint_address();
224  sample_info->external_callback_entry =
225  external_callback_entry_ptr == nullptr
226  ? nullptr
227  : reinterpret_cast<void*>(*external_callback_entry_ptr);
228  }
229 
230  i::SafeStackFrameIterator it(isolate, reinterpret_cast<i::Address>(regs->fp),
231  reinterpret_cast<i::Address>(regs->sp),
232  js_entry_sp);
233  if (it.done()) return true;
234 
235  size_t i = 0;
236  if (record_c_entry_frame == kIncludeCEntryFrame &&
237  (it.top_frame_type() == internal::StackFrame::EXIT ||
238  it.top_frame_type() == internal::StackFrame::BUILTIN_EXIT)) {
239  frames[i++] = reinterpret_cast<void*>(isolate->c_function());
240  }
241  i::RuntimeCallTimer* timer =
242  isolate->counters()->runtime_call_stats()->current_timer();
243  for (; !it.done() && i < frames_limit; it.Advance()) {
244  while (timer && reinterpret_cast<i::Address>(timer) < it.frame()->fp() &&
245  i < frames_limit) {
246  frames[i++] = reinterpret_cast<void*>(timer->counter());
247  timer = timer->parent();
248  }
249  if (i == frames_limit) break;
250  if (it.frame()->is_interpreted()) {
251  // For interpreted frames use the bytecode array pointer as the pc.
252  i::InterpretedFrame* frame =
253  static_cast<i::InterpretedFrame*>(it.frame());
254  // Since the sampler can interrupt execution at any point the
255  // bytecode_array might be garbage, so don't actually dereference it. We
256  // avoid the frame->GetXXX functions since they call BytecodeArray::cast,
257  // which has a heap access in its DCHECK.
258  i::Address bytecode_array = i::Memory<i::Address>(
259  frame->fp() + i::InterpreterFrameConstants::kBytecodeArrayFromFp);
260  i::Address bytecode_offset = i::Memory<i::Address>(
261  frame->fp() + i::InterpreterFrameConstants::kBytecodeOffsetFromFp);
262 
263  // If the bytecode array is a heap object and the bytecode offset is a
264  // Smi, use those, otherwise fall back to using the frame's pc.
265  if (HAS_HEAP_OBJECT_TAG(bytecode_array) && HAS_SMI_TAG(bytecode_offset)) {
266  frames[i++] = reinterpret_cast<void*>(
267  bytecode_array + i::Internals::SmiValue(bytecode_offset));
268  continue;
269  }
270  }
271  frames[i++] = reinterpret_cast<void*>(it.frame()->pc());
272  }
273  sample_info->frames_count = i;
274  return true;
275 }
276 
277 namespace internal {
278 
279 void TickSample::Init(Isolate* isolate, const v8::RegisterState& state,
280  RecordCEntryFrame record_c_entry_frame, bool update_stats,
281  bool use_simulator_reg_state) {
282  v8::TickSample::Init(reinterpret_cast<v8::Isolate*>(isolate), state,
283  record_c_entry_frame, update_stats,
284  use_simulator_reg_state);
285  if (pc == nullptr) return;
286  timestamp = base::TimeTicks::HighResolutionNow();
287 }
288 
289 } // namespace internal
290 } // namespace v8
void Init(Isolate *isolate, const v8::RegisterState &state, RecordCEntryFrame record_c_entry_frame, bool update_stats, bool use_simulator_reg_state=true)
Definition: tick-sample.cc:148
Definition: libplatform.h:13
static bool GetStackSample(Isolate *isolate, v8::RegisterState *state, RecordCEntryFrame record_c_entry_frame, void **frames, size_t frames_limit, v8::SampleInfo *sample_info, bool use_simulator_reg_state=true)
Definition: tick-sample.cc:183