Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(483)

Side by Side Diff: test/cctest/test-log-ia32.cc

Issue 50052: Support profiler stack sampling in any situation. (Closed)
Patch Set: Fixes according to comments Created 11 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « src/platform-win32.cc ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2006-2009 the V8 project authors. All rights reserved. 1 // Copyright 2006-2009 the V8 project authors. All rights reserved.
2 // 2 //
3 // Tests of profiler-related functions from log.h 3 // Tests of profiler-related functions from log.h
4 4
5 #ifdef ENABLE_LOGGING_AND_PROFILING 5 #ifdef ENABLE_LOGGING_AND_PROFILING
6 6
7 #include <stdlib.h> 7 #include <stdlib.h>
8 8
9 #include "v8.h" 9 #include "v8.h"
10 10
11 #include "log.h" 11 #include "log.h"
12 #include "top.h"
12 #include "cctest.h" 13 #include "cctest.h"
13 14
14 using v8::Function; 15 using v8::Function;
15 using v8::Local; 16 using v8::Local;
16 using v8::Object; 17 using v8::Object;
17 using v8::Script; 18 using v8::Script;
18 using v8::String; 19 using v8::String;
19 using v8::Value; 20 using v8::Value;
20 21
21 using v8::internal::byte; 22 using v8::internal::byte;
22 using v8::internal::Handle; 23 using v8::internal::Handle;
23 using v8::internal::JSFunction; 24 using v8::internal::JSFunction;
24 using v8::internal::StackTracer; 25 using v8::internal::StackTracer;
25 using v8::internal::TickSample; 26 using v8::internal::TickSample;
27 using v8::internal::Top;
26 28
27 29
28 static v8::Persistent<v8::Context> env; 30 static v8::Persistent<v8::Context> env;
29 31
30 32
31 static struct { 33 static struct {
32 StackTracer* tracer; 34 StackTracer* tracer;
33 TickSample* sample; 35 TickSample* sample;
34 } trace_env; 36 } trace_env = { NULL, NULL };
35 37
36 38
37 static void InitTraceEnv(StackTracer* tracer, TickSample* sample) { 39 static void InitTraceEnv(StackTracer* tracer, TickSample* sample) {
38 trace_env.tracer = tracer; 40 trace_env.tracer = tracer;
39 trace_env.sample = sample; 41 trace_env.sample = sample;
40 } 42 }
41 43
42 44
43 static void DoTrace(unsigned int fp) { 45 static void DoTrace(unsigned int fp) {
44 trace_env.sample->fp = fp; 46 trace_env.sample->fp = fp;
45 // something that is less than fp 47 // sp is only used to define stack high bound
46 trace_env.sample->sp = trace_env.sample->fp - 100; 48 trace_env.sample->sp =
49 reinterpret_cast<unsigned int>(trace_env.sample) - 10240;
47 trace_env.tracer->Trace(trace_env.sample); 50 trace_env.tracer->Trace(trace_env.sample);
48 } 51 }
49 52
50 53
51 static void CFuncDoTrace() { 54 // Hide c_entry_fp to emulate situation when sampling is done while
52 unsigned int fp; 55 // pure JS code is being executed
53 #ifdef __GNUC__ 56 static void DoTraceHideCEntryFPAddress(unsigned int fp) {
54 fp = reinterpret_cast<unsigned int>(__builtin_frame_address(0)); 57 v8::internal::Address saved_c_frame_fp = *(Top::c_entry_fp_address());
55 #elif defined _MSC_VER 58 CHECK(saved_c_frame_fp);
56 __asm mov [fp], ebp // NOLINT 59 *(Top::c_entry_fp_address()) = 0;
57 #endif
58 DoTrace(fp); 60 DoTrace(fp);
61 *(Top::c_entry_fp_address()) = saved_c_frame_fp;
59 } 62 }
60 63
61 64
62 static void CFunc(int i) { 65 static void CheckRetAddrIsInFunction(const char* func_name,
63 for (int j = i; j >= 0; --j) { 66 unsigned int ret_addr,
64 CFuncDoTrace();
65 }
66 }
67
68
69 static void CheckRetAddrIsInFunction(unsigned int ret_addr,
70 unsigned int func_start_addr, 67 unsigned int func_start_addr,
71 unsigned int func_len) { 68 unsigned int func_len) {
72 printf("CheckRetAddrIsInFunction: %08x %08x %08x\n", 69 printf("CheckRetAddrIsInFunction \"%s\": %08x %08x %08x\n",
73 func_start_addr, ret_addr, func_start_addr + func_len); 70 func_name, func_start_addr, ret_addr, func_start_addr + func_len);
74 CHECK_GE(ret_addr, func_start_addr); 71 CHECK_GE(ret_addr, func_start_addr);
75 CHECK_GE(func_start_addr + func_len, ret_addr); 72 CHECK_GE(func_start_addr + func_len, ret_addr);
76 } 73 }
77 74
78 75
79 #ifdef DEBUG 76 static void CheckRetAddrIsInJSFunction(const char* func_name,
80 static const int kMaxCFuncLen = 0x40; // seems enough for a small C function 77 unsigned int ret_addr,
81 78 Handle<JSFunction> func) {
82 static void CheckRetAddrIsInCFunction(unsigned int ret_addr, 79 v8::internal::Code* func_code = func->code();
83 unsigned int func_start_addr) { 80 CheckRetAddrIsInFunction(
84 CheckRetAddrIsInFunction(ret_addr, func_start_addr, kMaxCFuncLen); 81 func_name, ret_addr,
85 } 82 reinterpret_cast<unsigned int>(func_code->instruction_start()),
86 #endif 83 func_code->ExecutableSize());
87
88
89 TEST(PureCStackTrace) {
90 TickSample sample;
91 StackTracer tracer(reinterpret_cast<unsigned int>(&sample));
92 InitTraceEnv(&tracer, &sample);
93 CFunc(0);
94 #ifdef DEBUG
95 // C stack trace works only in debug mode, in release mode EBP is
96 // usually treated as a general-purpose register
97 CHECK_GT(sample.frames_count, 0);
98 CheckRetAddrIsInCFunction(reinterpret_cast<unsigned int>(sample.stack[0]),
99 reinterpret_cast<unsigned int>(&CFunc));
100 #endif
101 } 84 }
102 85
103 86
104 // --- T r a c e E x t e n s i o n --- 87 // --- T r a c e E x t e n s i o n ---
105 88
106 class TraceExtension : public v8::Extension { 89 class TraceExtension : public v8::Extension {
107 public: 90 public:
108 TraceExtension() : v8::Extension("v8/trace", kSource) { } 91 TraceExtension() : v8::Extension("v8/trace", kSource) { }
109 virtual v8::Handle<v8::FunctionTemplate> GetNativeFunction( 92 virtual v8::Handle<v8::FunctionTemplate> GetNativeFunction(
110 v8::Handle<v8::String> name); 93 v8::Handle<String> name);
111 static v8::Handle<v8::Value> Trace(const v8::Arguments& args); 94 static v8::Handle<v8::Value> Trace(const v8::Arguments& args);
95 static v8::Handle<v8::Value> JSTrace(const v8::Arguments& args);
112 private: 96 private:
97 static unsigned int GetFP(const v8::Arguments& args);
113 static const char* kSource; 98 static const char* kSource;
114 }; 99 };
115 100
116 101
117 const char* TraceExtension::kSource = "native function trace();"; 102 const char* TraceExtension::kSource =
103 "native function trace();"
104 "native function js_trace();";
118 105
119 106
120 v8::Handle<v8::FunctionTemplate> TraceExtension::GetNativeFunction( 107 v8::Handle<v8::FunctionTemplate> TraceExtension::GetNativeFunction(
121 v8::Handle<v8::String> str) { 108 v8::Handle<String> name) {
122 return v8::FunctionTemplate::New(TraceExtension::Trace); 109 if (name->Equals(String::New("trace"))) {
110 return v8::FunctionTemplate::New(TraceExtension::Trace);
111 } else if (name->Equals(String::New("js_trace"))) {
112 return v8::FunctionTemplate::New(TraceExtension::JSTrace);
113 } else {
114 CHECK(false);
115 return v8::Handle<v8::FunctionTemplate>();
116 }
117 }
118
119
120 unsigned int TraceExtension::GetFP(const v8::Arguments& args) {
121 CHECK_EQ(1, args.Length());
122 unsigned int fp = args[0]->Int32Value() << 2;
123 printf("Trace: %08x\n", fp);
124 return fp;
123 } 125 }
124 126
125 127
126 v8::Handle<v8::Value> TraceExtension::Trace(const v8::Arguments& args) { 128 v8::Handle<v8::Value> TraceExtension::Trace(const v8::Arguments& args) {
127 CHECK_EQ(1, args.Length()); 129 DoTrace(GetFP(args));
128 unsigned int fp = args[0]->Int32Value() << 2;
129 printf("Trace: %08x\n", fp);
130 DoTrace(fp);
131 return v8::Undefined(); 130 return v8::Undefined();
132 } 131 }
133 132
133
134 v8::Handle<v8::Value> TraceExtension::JSTrace(const v8::Arguments& args) {
135 DoTraceHideCEntryFPAddress(GetFP(args));
136 return v8::Undefined();
137 }
138
134 139
135 static TraceExtension kTraceExtension; 140 static TraceExtension kTraceExtension;
136 v8::DeclareExtension kTraceExtensionDeclaration(&kTraceExtension); 141 v8::DeclareExtension kTraceExtensionDeclaration(&kTraceExtension);
137 142
138 143
139 static void InitializeVM() { 144 static void InitializeVM() {
140 if (env.IsEmpty()) { 145 if (env.IsEmpty()) {
141 v8::HandleScope scope; 146 v8::HandleScope scope;
142 const char* extensions[] = { "v8/trace" }; 147 const char* extensions[] = { "v8/trace" };
143 v8::ExtensionConfiguration config(1, extensions); 148 v8::ExtensionConfiguration config(1, extensions);
(...skipping 12 matching lines...) Expand all
156 static void CompileRun(const char* source) { 161 static void CompileRun(const char* source) {
157 Script::Compile(String::New(source))->Run(); 162 Script::Compile(String::New(source))->Run();
158 } 163 }
159 164
160 165
161 static Local<Value> GetGlobalProperty(const char* name) { 166 static Local<Value> GetGlobalProperty(const char* name) {
162 return env->Global()->Get(String::New(name)); 167 return env->Global()->Get(String::New(name));
163 } 168 }
164 169
165 170
171 static Handle<JSFunction> GetGlobalJSFunction(const char* name) {
172 Handle<JSFunction> js_func(JSFunction::cast(
173 *(v8::Utils::OpenHandle(
174 *GetGlobalProperty(name)))));
175 return js_func;
176 }
177
178
179 static void CheckRetAddrIsInJSFunction(const char* func_name,
180 unsigned int ret_addr) {
181 CheckRetAddrIsInJSFunction(func_name, ret_addr,
182 GetGlobalJSFunction(func_name));
183 }
184
185
166 static void SetGlobalProperty(const char* name, Local<Value> value) { 186 static void SetGlobalProperty(const char* name, Local<Value> value) {
167 env->Global()->Set(String::New(name), value); 187 env->Global()->Set(String::New(name), value);
168 } 188 }
169 189
170 190
171 static bool Patch(byte* from, 191 static bool Patch(byte* from,
172 size_t num, 192 size_t num,
173 byte* original, 193 byte* original,
174 byte* patch, 194 byte* patch,
175 size_t patch_len) { 195 size_t patch_len) {
176 byte* to = from + num; 196 byte* to = from + num;
177 do { 197 do {
178 from = static_cast<byte*>(memchr(from, *original, to - from)); 198 from = static_cast<byte*>(memchr(from, *original, to - from));
179 CHECK(from != NULL); 199 CHECK(from != NULL);
180 if (memcmp(original, from, patch_len) == 0) { 200 if (memcmp(original, from, patch_len) == 0) {
181 memcpy(from, patch, patch_len); 201 memcpy(from, patch, patch_len);
182 return true; 202 return true;
183 } else { 203 } else {
184 from++; 204 from++;
185 } 205 }
186 } while (to - from > 0); 206 } while (to - from > 0);
187 return false; 207 return false;
188 } 208 }
189 209
190 210
191 TEST(PureJSStackTrace) { 211 // Creates a global function named 'func_name' that calls the tracing
192 TickSample sample; 212 // function 'trace_func_name' with an actual EBP register value,
193 StackTracer tracer(reinterpret_cast<unsigned int>(&sample)); 213 // shifted right to be presented as Smi.
194 InitTraceEnv(&tracer, &sample); 214 static void CreateTraceCallerFunction(const char* func_name,
195 215 const char* trace_func_name) {
196 InitializeVM(); 216 ::v8::internal::EmbeddedVector<char, 256> trace_call_buf;
197 v8::HandleScope scope; 217 ::v8::internal::OS::SNPrintF(trace_call_buf, "%s(0x6666);", trace_func_name);
198 Handle<JSFunction> call_trace = CompileFunction("trace(0x6666);"); 218 Handle<JSFunction> func = CompileFunction(trace_call_buf.start());
199 CHECK(!call_trace.is_null()); 219 CHECK(!func.is_null());
200 v8::internal::Code* call_trace_code = call_trace->code(); 220 v8::internal::Code* func_code = func->code();
201 CHECK(call_trace_code->IsCode()); 221 CHECK(func_code->IsCode());
202 222
203 // push 0xcccc (= 0x6666 << 1) 223 // push 0xcccc (= 0x6666 << 1)
204 byte original[] = { 0x68, 0xcc, 0xcc, 0x00, 0x00 }; 224 byte original[] = { 0x68, 0xcc, 0xcc, 0x00, 0x00 };
205 // mov eax,ebp; shr eax; push eax; 225 // mov eax,ebp; shr eax; push eax;
206 byte patch[] = { 0x89, 0xe8, 0xd1, 0xe8, 0x50 }; 226 byte patch[] = { 0x89, 0xe8, 0xd1, 0xe8, 0x50 };
207 // Patch generated code to replace pushing of a constant with 227 // Patch generated code to replace pushing of a constant with
208 // pushing of ebp contents in a Smi 228 // pushing of ebp contents in a Smi
209 CHECK(Patch(call_trace_code->instruction_start(), 229 CHECK(Patch(func_code->instruction_start(),
210 call_trace_code->instruction_size(), 230 func_code->instruction_size(),
211 original, patch, sizeof(patch))); 231 original, patch, sizeof(patch)));
212 232
213 SetGlobalProperty("JSFuncDoTrace", v8::ToApi<Value>(call_trace)); 233 SetGlobalProperty(func_name, v8::ToApi<Value>(func));
234 }
214 235
236
237 TEST(CFromJSStackTrace) {
238 TickSample sample;
239 StackTracer tracer(reinterpret_cast<unsigned int>(&sample));
240 InitTraceEnv(&tracer, &sample);
241
242 InitializeVM();
243 v8::HandleScope scope;
244 CreateTraceCallerFunction("JSFuncDoTrace", "trace");
215 CompileRun( 245 CompileRun(
216 "function JSTrace() {" 246 "function JSTrace() {"
217 " JSFuncDoTrace();" 247 " JSFuncDoTrace();"
218 "};\n" 248 "};\n"
219 "JSTrace();"); 249 "JSTrace();");
220 CHECK_GT(sample.frames_count, 1); 250 CHECK_GT(sample.frames_count, 1);
221 CheckRetAddrIsInFunction( 251 // Stack sampling will start from the first JS function, i.e. "JSFuncDoTrace"
222 reinterpret_cast<unsigned int>(sample.stack[0]), 252 CheckRetAddrIsInJSFunction("JSFuncDoTrace",
223 reinterpret_cast<unsigned int>(call_trace_code->instruction_start()), 253 reinterpret_cast<unsigned int>(sample.stack[0]));
224 call_trace_code->instruction_size()); 254 CheckRetAddrIsInJSFunction("JSTrace",
225 Handle<JSFunction> js_trace(JSFunction::cast(*(v8::Utils::OpenHandle( 255 reinterpret_cast<unsigned int>(sample.stack[1]));
226 *GetGlobalProperty("JSTrace")))));
227 v8::internal::Code* js_trace_code = js_trace->code();
228 CheckRetAddrIsInFunction(
229 reinterpret_cast<unsigned int>(sample.stack[1]),
230 reinterpret_cast<unsigned int>(js_trace_code->instruction_start()),
231 js_trace_code->instruction_size());
232 } 256 }
233 257
258
259 TEST(PureJSStackTrace) {
260 TickSample sample;
261 StackTracer tracer(reinterpret_cast<unsigned int>(&sample));
262 InitTraceEnv(&tracer, &sample);
263
264 InitializeVM();
265 v8::HandleScope scope;
266 CreateTraceCallerFunction("JSFuncDoTrace", "js_trace");
267 CompileRun(
268 "function JSTrace() {"
269 " JSFuncDoTrace();"
270 "};\n"
271 "function OuterJSTrace() {"
272 " JSTrace();"
273 "};\n"
274 "OuterJSTrace();");
275 CHECK_GT(sample.frames_count, 1);
276 // Stack sampling will start from the caller of JSFuncDoTrace, i.e. "JSTrace"
277 CheckRetAddrIsInJSFunction("JSTrace",
278 reinterpret_cast<unsigned int>(sample.stack[0]));
279 CheckRetAddrIsInJSFunction("OuterJSTrace",
280 reinterpret_cast<unsigned int>(sample.stack[1]));
281 }
282
283
284 static void CFuncDoTrace() {
285 unsigned int fp;
286 #ifdef __GNUC__
287 fp = reinterpret_cast<unsigned int>(__builtin_frame_address(0));
288 #elif defined _MSC_VER
289 __asm mov [fp], ebp // NOLINT
290 #endif
291 DoTrace(fp);
292 }
293
294
295 static int CFunc(int depth) {
296 if (depth <= 0) {
297 CFuncDoTrace();
298 return 0;
299 } else {
300 return CFunc(depth - 1) + 1;
301 }
302 }
303
304
305 TEST(PureCStackTrace) {
306 TickSample sample;
307 StackTracer tracer(reinterpret_cast<unsigned int>(&sample));
308 InitTraceEnv(&tracer, &sample);
309 // Check that sampler doesn't crash
310 CHECK_EQ(10, CFunc(10));
311 }
312
313
234 #endif // ENABLE_LOGGING_AND_PROFILING 314 #endif // ENABLE_LOGGING_AND_PROFILING
OLDNEW
« no previous file with comments | « src/platform-win32.cc ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698