• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "src/frames-inl.h"
8 #include "src/vm-state-inl.h"
9 
10 
11 namespace v8 {
12 namespace internal {
13 
14 namespace {
15 
IsSamePage(byte * ptr1,byte * ptr2)16 bool IsSamePage(byte* ptr1, byte* ptr2) {
17   const uint32_t kPageSize = 4096;
18   uintptr_t mask = ~static_cast<uintptr_t>(kPageSize - 1);
19   return (reinterpret_cast<uintptr_t>(ptr1) & mask) ==
20          (reinterpret_cast<uintptr_t>(ptr2) & mask);
21 }
22 
23 // Check if the code at specified address could potentially be a
24 // frame setup code.
IsNoFrameRegion(Address address)25 bool IsNoFrameRegion(Address address) {
26   struct Pattern {
27     int bytes_count;
28     byte bytes[8];
29     int offsets[4];
30   };
31   byte* pc = reinterpret_cast<byte*>(address);
32   static Pattern patterns[] = {
33 #if V8_HOST_ARCH_IA32
34     // push %ebp
35     // mov %esp,%ebp
36     {3, {0x55, 0x89, 0xe5}, {0, 1, -1}},
37     // pop %ebp
38     // ret N
39     {2, {0x5d, 0xc2}, {0, 1, -1}},
40     // pop %ebp
41     // ret
42     {2, {0x5d, 0xc3}, {0, 1, -1}},
43 #elif V8_HOST_ARCH_X64
44     // pushq %rbp
45     // movq %rsp,%rbp
46     {4, {0x55, 0x48, 0x89, 0xe5}, {0, 1, -1}},
47     // popq %rbp
48     // ret N
49     {2, {0x5d, 0xc2}, {0, 1, -1}},
50     // popq %rbp
51     // ret
52     {2, {0x5d, 0xc3}, {0, 1, -1}},
53 #endif
54     {0, {}, {}}
55   };
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(pc, pc - 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 //
80 // StackTracer implementation
81 //
Init(Isolate * isolate,const v8::RegisterState & regs,RecordCEntryFrame record_c_entry_frame,bool update_stats)82 DISABLE_ASAN void TickSample::Init(Isolate* isolate,
83                                    const v8::RegisterState& regs,
84                                    RecordCEntryFrame record_c_entry_frame,
85                                    bool update_stats) {
86   timestamp = base::TimeTicks::HighResolutionNow();
87   this->update_stats = update_stats;
88 
89   SampleInfo info;
90   if (GetStackSample(isolate, regs, record_c_entry_frame,
91                      reinterpret_cast<void**>(&stack[0]), kMaxFramesCount,
92                      &info)) {
93     state = info.vm_state;
94     pc = static_cast<Address>(regs.pc);
95     frames_count = static_cast<unsigned>(info.frames_count);
96     has_external_callback = info.external_callback_entry != nullptr;
97     if (has_external_callback) {
98       external_callback_entry =
99           static_cast<Address>(info.external_callback_entry);
100     } else if (frames_count) {
101       // sp register may point at an arbitrary place in memory, make
102       // sure MSAN doesn't complain about it.
103       MSAN_MEMORY_IS_INITIALIZED(regs.sp, sizeof(Address));
104       // Sample potential return address value for frameless invocation of
105       // stubs (we'll figure out later, if this value makes sense).
106       tos = Memory::Address_at(reinterpret_cast<Address>(regs.sp));
107     } else {
108       tos = nullptr;
109     }
110   } else {
111     // It is executing JS but failed to collect a stack trace.
112     // Mark the sample as spoiled.
113     timestamp = base::TimeTicks();
114     pc = nullptr;
115   }
116 }
117 
GetStackSample(Isolate * isolate,const v8::RegisterState & regs,RecordCEntryFrame record_c_entry_frame,void ** frames,size_t frames_limit,v8::SampleInfo * sample_info)118 bool TickSample::GetStackSample(Isolate* isolate, const v8::RegisterState& regs,
119                                 RecordCEntryFrame record_c_entry_frame,
120                                 void** frames, size_t frames_limit,
121                                 v8::SampleInfo* sample_info) {
122   sample_info->frames_count = 0;
123   sample_info->vm_state = isolate->current_vm_state();
124   sample_info->external_callback_entry = nullptr;
125   if (sample_info->vm_state == GC) return true;
126 
127   Address js_entry_sp = isolate->js_entry_sp();
128   if (js_entry_sp == 0) return true;  // Not executing JS now.
129 
130   if (regs.pc && IsNoFrameRegion(static_cast<Address>(regs.pc))) {
131     // Can't collect stack.
132     return false;
133   }
134 
135   ExternalCallbackScope* scope = isolate->external_callback_scope();
136   Address handler = Isolate::handler(isolate->thread_local_top());
137   // If there is a handler on top of the external callback scope then
138   // we have already entrered JavaScript again and the external callback
139   // is not the top function.
140   if (scope && scope->scope_address() < handler) {
141     sample_info->external_callback_entry =
142         *scope->callback_entrypoint_address();
143   }
144 
145   SafeStackFrameIterator it(isolate, reinterpret_cast<Address>(regs.fp),
146                             reinterpret_cast<Address>(regs.sp), js_entry_sp);
147   size_t i = 0;
148   if (record_c_entry_frame == kIncludeCEntryFrame && !it.done() &&
149       it.top_frame_type() == StackFrame::EXIT) {
150     frames[i++] = isolate->c_function();
151   }
152   while (!it.done() && i < frames_limit) {
153     if (it.frame()->is_interpreted()) {
154       // For interpreted frames use the bytecode array pointer as the pc.
155       InterpretedFrame* frame = static_cast<InterpretedFrame*>(it.frame());
156       // Since the sampler can interrupt execution at any point the
157       // bytecode_array might be garbage, so don't dereference it.
158       Address bytecode_array =
159           reinterpret_cast<Address>(frame->GetBytecodeArray()) - kHeapObjectTag;
160       frames[i++] = bytecode_array + BytecodeArray::kHeaderSize +
161                     frame->GetBytecodeOffset();
162     } else {
163       frames[i++] = it.frame()->pc();
164     }
165     it.Advance();
166   }
167   sample_info->frames_count = i;
168   return true;
169 }
170 
171 #if defined(USE_SIMULATOR)
FillRegisters(Isolate * isolate,v8::RegisterState * state)172 bool SimulatorHelper::FillRegisters(Isolate* isolate,
173                                     v8::RegisterState* state) {
174   Simulator *simulator = isolate->thread_local_top()->simulator_;
175   // Check if there is active simulator.
176   if (simulator == NULL) return false;
177 #if V8_TARGET_ARCH_ARM
178   if (!simulator->has_bad_pc()) {
179     state->pc = reinterpret_cast<Address>(simulator->get_pc());
180   }
181   state->sp = reinterpret_cast<Address>(simulator->get_register(Simulator::sp));
182   state->fp = reinterpret_cast<Address>(simulator->get_register(
183       Simulator::r11));
184 #elif V8_TARGET_ARCH_ARM64
185   state->pc = reinterpret_cast<Address>(simulator->pc());
186   state->sp = reinterpret_cast<Address>(simulator->sp());
187   state->fp = reinterpret_cast<Address>(simulator->fp());
188 #elif V8_TARGET_ARCH_MIPS || V8_TARGET_ARCH_MIPS64
189   if (!simulator->has_bad_pc()) {
190     state->pc = reinterpret_cast<Address>(simulator->get_pc());
191   }
192   state->sp = reinterpret_cast<Address>(simulator->get_register(Simulator::sp));
193   state->fp = reinterpret_cast<Address>(simulator->get_register(Simulator::fp));
194 #elif V8_TARGET_ARCH_PPC
195   if (!simulator->has_bad_pc()) {
196     state->pc = reinterpret_cast<Address>(simulator->get_pc());
197   }
198   state->sp = reinterpret_cast<Address>(simulator->get_register(Simulator::sp));
199   state->fp = reinterpret_cast<Address>(simulator->get_register(Simulator::fp));
200 #elif V8_TARGET_ARCH_S390
201   if (!simulator->has_bad_pc()) {
202     state->pc = reinterpret_cast<Address>(simulator->get_pc());
203   }
204   state->sp = reinterpret_cast<Address>(simulator->get_register(Simulator::sp));
205   state->fp = reinterpret_cast<Address>(simulator->get_register(Simulator::fp));
206 #endif
207   if (state->sp == 0 || state->fp == 0) {
208     // It possible that the simulator is interrupted while it is updating
209     // the sp or fp register. ARM64 simulator does this in two steps:
210     // first setting it to zero and then setting it to the new value.
211     // Bailout if sp/fp doesn't contain the new value.
212     //
213     // FIXME: The above doesn't really solve the issue.
214     // If a 64-bit target is executed on a 32-bit host even the final
215     // write is non-atomic, so it might obtain a half of the result.
216     // Moreover as long as the register set code uses memcpy (as of now),
217     // it is not guaranteed to be atomic even when both host and target
218     // are of same bitness.
219     return false;
220   }
221   return true;
222 }
223 #endif  // USE_SIMULATOR
224 
225 }  // namespace internal
226 }  // namespace v8
227