blob: 710c10e996a8b235e6bed159febc683c275c4e64 [file] [log] [blame]
Steve Blocka7e24c12009-10-30 11:49:00 +00001// Copyright 2006-2009 the V8 project authors. All rights reserved.
2//
3// Tests of logging functions from log.h
4
5#ifdef ENABLE_LOGGING_AND_PROFILING
6
7#ifdef __linux__
8#include <math.h>
9#include <pthread.h>
10#include <signal.h>
11#include <unistd.h>
12#endif // __linux__
13
14#include "v8.h"
15#include "log.h"
Steve Block6ded16b2010-05-10 14:33:55 +010016#include "cpu-profiler.h"
Steve Blocka7e24c12009-10-30 11:49:00 +000017#include "v8threads.h"
18#include "cctest.h"
19
20using v8::internal::Address;
21using v8::internal::EmbeddedVector;
22using v8::internal::Logger;
Steve Blockd0582a62009-12-15 09:54:21 +000023using v8::internal::StrLength;
Steve Blocka7e24c12009-10-30 11:49:00 +000024
25namespace i = v8::internal;
26
27static void SetUp() {
28 // Log to memory buffer.
29 i::FLAG_logfile = "*";
30 i::FLAG_log = true;
31 Logger::Setup();
32}
33
34static void TearDown() {
35 Logger::TearDown();
36}
37
38
39TEST(EmptyLog) {
40 SetUp();
41 CHECK_EQ(0, Logger::GetLogLines(0, NULL, 0));
42 CHECK_EQ(0, Logger::GetLogLines(100, NULL, 0));
43 CHECK_EQ(0, Logger::GetLogLines(0, NULL, 100));
44 CHECK_EQ(0, Logger::GetLogLines(100, NULL, 100));
45 TearDown();
46}
47
48
49TEST(GetMessages) {
50 SetUp();
51 Logger::StringEvent("aaa", "bbb");
52 Logger::StringEvent("cccc", "dddd");
53 CHECK_EQ(0, Logger::GetLogLines(0, NULL, 0));
54 char log_lines[100];
55 memset(log_lines, 0, sizeof(log_lines));
Steve Blocka7e24c12009-10-30 11:49:00 +000056 // See Logger::StringEvent.
57 const char* line_1 = "aaa,\"bbb\"\n";
Steve Blockd0582a62009-12-15 09:54:21 +000058 const int line_1_len = StrLength(line_1);
Steve Blocka7e24c12009-10-30 11:49:00 +000059 // The exact size.
60 CHECK_EQ(line_1_len, Logger::GetLogLines(0, log_lines, line_1_len));
61 CHECK_EQ(line_1, log_lines);
62 memset(log_lines, 0, sizeof(log_lines));
63 // A bit more than the first line length.
64 CHECK_EQ(line_1_len, Logger::GetLogLines(0, log_lines, line_1_len + 3));
65 log_lines[line_1_len] = '\0';
66 CHECK_EQ(line_1, log_lines);
67 memset(log_lines, 0, sizeof(log_lines));
68 const char* line_2 = "cccc,\"dddd\"\n";
Steve Blockd0582a62009-12-15 09:54:21 +000069 const int line_2_len = StrLength(line_2);
Steve Blocka7e24c12009-10-30 11:49:00 +000070 // Now start with line_2 beginning.
71 CHECK_EQ(0, Logger::GetLogLines(line_1_len, log_lines, 0));
Steve Blocka7e24c12009-10-30 11:49:00 +000072 CHECK_EQ(line_2_len, Logger::GetLogLines(line_1_len, log_lines, line_2_len));
73 CHECK_EQ(line_2, log_lines);
74 memset(log_lines, 0, sizeof(log_lines));
75 CHECK_EQ(line_2_len,
76 Logger::GetLogLines(line_1_len, log_lines, line_2_len + 3));
77 CHECK_EQ(line_2, log_lines);
78 memset(log_lines, 0, sizeof(log_lines));
79 // Now get entire buffer contents.
80 const char* all_lines = "aaa,\"bbb\"\ncccc,\"dddd\"\n";
Steve Blockd0582a62009-12-15 09:54:21 +000081 const int all_lines_len = StrLength(all_lines);
Steve Blocka7e24c12009-10-30 11:49:00 +000082 CHECK_EQ(all_lines_len, Logger::GetLogLines(0, log_lines, all_lines_len));
83 CHECK_EQ(all_lines, log_lines);
84 memset(log_lines, 0, sizeof(log_lines));
85 CHECK_EQ(all_lines_len, Logger::GetLogLines(0, log_lines, all_lines_len + 3));
86 CHECK_EQ(all_lines, log_lines);
87 memset(log_lines, 0, sizeof(log_lines));
88 TearDown();
89}
90
91
92static int GetLogLines(int start_pos, i::Vector<char>* buffer) {
93 return Logger::GetLogLines(start_pos, buffer->start(), buffer->length());
94}
95
96
97TEST(BeyondWritePosition) {
98 SetUp();
99 Logger::StringEvent("aaa", "bbb");
100 Logger::StringEvent("cccc", "dddd");
101 // See Logger::StringEvent.
102 const char* all_lines = "aaa,\"bbb\"\ncccc,\"dddd\"\n";
Steve Blockd0582a62009-12-15 09:54:21 +0000103 const int all_lines_len = StrLength(all_lines);
Steve Blocka7e24c12009-10-30 11:49:00 +0000104 EmbeddedVector<char, 100> buffer;
105 const int beyond_write_pos = all_lines_len;
106 CHECK_EQ(0, Logger::GetLogLines(beyond_write_pos, buffer.start(), 1));
107 CHECK_EQ(0, GetLogLines(beyond_write_pos, &buffer));
108 CHECK_EQ(0, Logger::GetLogLines(beyond_write_pos + 1, buffer.start(), 1));
109 CHECK_EQ(0, GetLogLines(beyond_write_pos + 1, &buffer));
110 CHECK_EQ(0, Logger::GetLogLines(beyond_write_pos + 100, buffer.start(), 1));
111 CHECK_EQ(0, GetLogLines(beyond_write_pos + 100, &buffer));
112 CHECK_EQ(0, Logger::GetLogLines(10 * 1024 * 1024, buffer.start(), 1));
113 CHECK_EQ(0, GetLogLines(10 * 1024 * 1024, &buffer));
114 TearDown();
115}
116
117
118TEST(MemoryLoggingTurnedOff) {
119 // Log to stdout
120 i::FLAG_logfile = "-";
121 i::FLAG_log = true;
122 Logger::Setup();
123 CHECK_EQ(0, Logger::GetLogLines(0, NULL, 0));
124 CHECK_EQ(0, Logger::GetLogLines(100, NULL, 0));
125 CHECK_EQ(0, Logger::GetLogLines(0, NULL, 100));
126 CHECK_EQ(0, Logger::GetLogLines(100, NULL, 100));
127 Logger::TearDown();
128}
129
130
131static void CompileAndRunScript(const char *src) {
132 v8::Script::Compile(v8::String::New(src))->Run();
133}
134
135
136namespace v8 {
137namespace internal {
138
139class LoggerTestHelper : public AllStatic {
140 public:
141 static bool IsSamplerActive() { return Logger::IsProfilerSamplerActive(); }
Shimeng (Simon) Wang8a31eba2010-12-06 19:01:33 -0800142 static void ResetSamplesTaken() {
143 reinterpret_cast<Sampler*>(Logger::ticker_)->ResetSamplesTaken();
144 }
145 static bool has_samples_taken() {
146 return reinterpret_cast<Sampler*>(Logger::ticker_)->samples_taken() > 0;
147 }
Steve Blocka7e24c12009-10-30 11:49:00 +0000148};
149
150} // namespace v8::internal
151} // namespace v8
152
153using v8::internal::LoggerTestHelper;
154
155
Andrei Popescu402d9372010-02-26 13:31:12 +0000156namespace {
157
158class ScopedLoggerInitializer {
159 public:
Steve Block6ded16b2010-05-10 14:33:55 +0100160 explicit ScopedLoggerInitializer(bool prof_lazy)
161 : saved_prof_lazy_(i::FLAG_prof_lazy),
Andrei Popescu402d9372010-02-26 13:31:12 +0000162 saved_prof_(i::FLAG_prof),
163 saved_prof_auto_(i::FLAG_prof_auto),
Steve Block6ded16b2010-05-10 14:33:55 +0100164 trick_to_run_init_flags_(init_flags_(prof_lazy)),
Andrei Popescu402d9372010-02-26 13:31:12 +0000165 need_to_set_up_logger_(i::V8::IsRunning()),
166 scope_(),
167 env_(v8::Context::New()) {
168 if (need_to_set_up_logger_) Logger::Setup();
169 env_->Enter();
170 }
171
172 ~ScopedLoggerInitializer() {
173 env_->Exit();
174 Logger::TearDown();
175 i::FLAG_prof_lazy = saved_prof_lazy_;
176 i::FLAG_prof = saved_prof_;
177 i::FLAG_prof_auto = saved_prof_auto_;
Andrei Popescu402d9372010-02-26 13:31:12 +0000178 }
179
180 v8::Handle<v8::Context>& env() { return env_; }
181
182 private:
Steve Block6ded16b2010-05-10 14:33:55 +0100183 static bool init_flags_(bool prof_lazy) {
Andrei Popescu402d9372010-02-26 13:31:12 +0000184 i::FLAG_prof = true;
185 i::FLAG_prof_lazy = prof_lazy;
186 i::FLAG_prof_auto = false;
187 i::FLAG_logfile = "*";
188 return prof_lazy;
189 }
190
Andrei Popescu402d9372010-02-26 13:31:12 +0000191 const bool saved_prof_lazy_;
192 const bool saved_prof_;
193 const bool saved_prof_auto_;
194 const bool trick_to_run_init_flags_;
195 const bool need_to_set_up_logger_;
196 v8::HandleScope scope_;
197 v8::Handle<v8::Context> env_;
198
199 DISALLOW_COPY_AND_ASSIGN(ScopedLoggerInitializer);
200};
201
202
203class LogBufferMatcher {
204 public:
205 LogBufferMatcher() {
206 // Skip all initially logged stuff.
207 log_pos_ = GetLogLines(0, &buffer_);
208 }
209
210 int log_pos() { return log_pos_; }
211
212 int GetNextChunk() {
213 int chunk_size = GetLogLines(log_pos_, &buffer_);
214 CHECK_GT(buffer_.length(), chunk_size);
215 buffer_[chunk_size] = '\0';
216 log_pos_ += chunk_size;
217 return chunk_size;
218 }
219
220 const char* Find(const char* substr) {
221 return strstr(buffer_.start(), substr);
222 }
223
224 const char* Find(const i::Vector<char>& substr) {
225 return Find(substr.start());
226 }
227
228 bool IsInSequence(const char* s1, const char* s2) {
229 const char* s1_pos = Find(s1);
230 const char* s2_pos = Find(s2);
231 CHECK_NE(NULL, s1_pos);
232 CHECK_NE(NULL, s2_pos);
233 return s1_pos < s2_pos;
234 }
235
236 void PrintBuffer() {
237 puts(buffer_.start());
238 }
239
240 private:
241 EmbeddedVector<char, 102400> buffer_;
242 int log_pos_;
243};
244
245} // namespace
246
247
248static void CheckThatProfilerWorks(LogBufferMatcher* matcher) {
Shimeng (Simon) Wang8a31eba2010-12-06 19:01:33 -0800249 CHECK(!LoggerTestHelper::IsSamplerActive());
250 LoggerTestHelper::ResetSamplesTaken();
251
Andrei Popescu402d9372010-02-26 13:31:12 +0000252 Logger::ResumeProfiler(v8::PROFILER_MODULE_CPU, 0);
Steve Blocka7e24c12009-10-30 11:49:00 +0000253 CHECK(LoggerTestHelper::IsSamplerActive());
254
255 // Verify that the current map of compiled functions has been logged.
Andrei Popescu402d9372010-02-26 13:31:12 +0000256 CHECK_GT(matcher->GetNextChunk(), 0);
Steve Blocka7e24c12009-10-30 11:49:00 +0000257 const char* code_creation = "\ncode-creation,"; // eq. to /^code-creation,/
Andrei Popescu402d9372010-02-26 13:31:12 +0000258 CHECK_NE(NULL, matcher->Find(code_creation));
Steve Blocka7e24c12009-10-30 11:49:00 +0000259
Steve Blocka7e24c12009-10-30 11:49:00 +0000260 // Force compiler to generate new code by parametrizing source.
261 EmbeddedVector<char, 100> script_src;
262 i::OS::SNPrintF(script_src,
Leon Clarked91b9f72010-01-27 17:25:45 +0000263 "function f%d(x) { return %d * x; }"
264 "for (var i = 0; i < 10000; ++i) { f%d(i); }",
Andrei Popescu402d9372010-02-26 13:31:12 +0000265 matcher->log_pos(), matcher->log_pos(), matcher->log_pos());
Steve Blocka7e24c12009-10-30 11:49:00 +0000266 // Run code for 200 msecs to get some ticks.
267 const double end_time = i::OS::TimeCurrentMillis() + 200;
268 while (i::OS::TimeCurrentMillis() < end_time) {
269 CompileAndRunScript(script_src.start());
270 // Yield CPU to give Profiler thread a chance to process ticks.
271 i::OS::Sleep(1);
272 }
273
Andrei Popescu402d9372010-02-26 13:31:12 +0000274 Logger::PauseProfiler(v8::PROFILER_MODULE_CPU, 0);
Steve Blocka7e24c12009-10-30 11:49:00 +0000275 CHECK(!LoggerTestHelper::IsSamplerActive());
276
277 // Wait 50 msecs to allow Profiler thread to process the last
278 // tick sample it has got.
279 i::OS::Sleep(50);
280
281 // Now we must have compiler and tick records.
Andrei Popescu402d9372010-02-26 13:31:12 +0000282 CHECK_GT(matcher->GetNextChunk(), 0);
283 matcher->PrintBuffer();
284 CHECK_NE(NULL, matcher->Find(code_creation));
Steve Blocka7e24c12009-10-30 11:49:00 +0000285 const char* tick = "\ntick,";
Andrei Popescu402d9372010-02-26 13:31:12 +0000286 const bool ticks_found = matcher->Find(tick) != NULL;
Shimeng (Simon) Wang8a31eba2010-12-06 19:01:33 -0800287 CHECK_EQ(LoggerTestHelper::has_samples_taken(), ticks_found);
Steve Blocka7e24c12009-10-30 11:49:00 +0000288}
289
290
291TEST(ProfLazyMode) {
Steve Block6ded16b2010-05-10 14:33:55 +0100292 ScopedLoggerInitializer initialize_logger(true);
Steve Blocka7e24c12009-10-30 11:49:00 +0000293
294 // No sampling should happen prior to resuming profiler.
295 CHECK(!LoggerTestHelper::IsSamplerActive());
296
Andrei Popescu402d9372010-02-26 13:31:12 +0000297 LogBufferMatcher matcher;
Steve Blockd0582a62009-12-15 09:54:21 +0000298 // Nothing must be logged until profiling is resumed.
Andrei Popescu402d9372010-02-26 13:31:12 +0000299 CHECK_EQ(0, matcher.log_pos());
Steve Blocka7e24c12009-10-30 11:49:00 +0000300
301 CompileAndRunScript("var a = (function(x) { return x + 1; })(10);");
302
303 // Nothing must be logged while profiling is suspended.
Andrei Popescu402d9372010-02-26 13:31:12 +0000304 CHECK_EQ(0, matcher.GetNextChunk());
Steve Blocka7e24c12009-10-30 11:49:00 +0000305
Andrei Popescu402d9372010-02-26 13:31:12 +0000306 CheckThatProfilerWorks(&matcher);
Steve Blocka7e24c12009-10-30 11:49:00 +0000307
308 CompileAndRunScript("var a = (function(x) { return x + 1; })(10);");
309
310 // No new data beyond last retrieved position.
Andrei Popescu402d9372010-02-26 13:31:12 +0000311 CHECK_EQ(0, matcher.GetNextChunk());
Steve Blocka7e24c12009-10-30 11:49:00 +0000312
313 // Check that profiling can be resumed again.
Andrei Popescu402d9372010-02-26 13:31:12 +0000314 CheckThatProfilerWorks(&matcher);
Steve Blocka7e24c12009-10-30 11:49:00 +0000315}
316
317
John Reck59135872010-11-02 12:39:01 -0700318// BUG(913). Need to implement support for profiling multiple VM threads.
319#if 0
Steve Blocka7e24c12009-10-30 11:49:00 +0000320
321namespace {
322
323class LoopingThread : public v8::internal::Thread {
324 public:
325 LoopingThread()
326 : v8::internal::Thread(),
327 semaphore_(v8::internal::OS::CreateSemaphore(0)),
328 run_(true) {
329 }
330
331 virtual ~LoopingThread() { delete semaphore_; }
332
333 void Run() {
334 self_ = pthread_self();
335 RunLoop();
336 }
337
338 void SendSigProf() { pthread_kill(self_, SIGPROF); }
339
340 void Stop() { run_ = false; }
341
342 bool WaitForRunning() { return semaphore_->Wait(1000000); }
343
344 protected:
345 bool IsRunning() { return run_; }
346
347 virtual void RunLoop() = 0;
348
349 void SetV8ThreadId() {
350 v8_thread_id_ = v8::V8::GetCurrentThreadId();
351 }
352
353 void SignalRunning() { semaphore_->Signal(); }
354
355 private:
356 v8::internal::Semaphore* semaphore_;
357 bool run_;
358 pthread_t self_;
359 int v8_thread_id_;
360};
361
362
363class LoopingJsThread : public LoopingThread {
364 public:
365 void RunLoop() {
Steve Block6ded16b2010-05-10 14:33:55 +0100366 v8::Locker locker;
367 CHECK(v8::internal::ThreadManager::HasId());
368 SetV8ThreadId();
Steve Blocka7e24c12009-10-30 11:49:00 +0000369 while (IsRunning()) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000370 v8::HandleScope scope;
371 v8::Persistent<v8::Context> context = v8::Context::New();
Steve Block6ded16b2010-05-10 14:33:55 +0100372 CHECK(!context.IsEmpty());
373 {
374 v8::Context::Scope context_scope(context);
375 SignalRunning();
376 CompileAndRunScript(
377 "var j; for (var i=0; i<10000; ++i) { j = Math.sin(i); }");
378 }
Steve Blocka7e24c12009-10-30 11:49:00 +0000379 context.Dispose();
380 i::OS::Sleep(1);
381 }
382 }
383};
384
385
386class LoopingNonJsThread : public LoopingThread {
387 public:
388 void RunLoop() {
389 v8::Locker locker;
390 v8::Unlocker unlocker;
391 // Now thread has V8's id, but will not run VM code.
392 CHECK(v8::internal::ThreadManager::HasId());
393 double i = 10;
394 SignalRunning();
395 while (IsRunning()) {
396 i = sin(i);
397 i::OS::Sleep(1);
398 }
399 }
400};
401
402
403class TestSampler : public v8::internal::Sampler {
404 public:
405 TestSampler()
406 : Sampler(0, true),
407 semaphore_(v8::internal::OS::CreateSemaphore(0)),
408 was_sample_stack_called_(false) {
409 }
410
411 ~TestSampler() { delete semaphore_; }
412
413 void SampleStack(v8::internal::TickSample*) {
414 was_sample_stack_called_ = true;
415 }
416
417 void Tick(v8::internal::TickSample*) { semaphore_->Signal(); }
418
419 bool WaitForTick() { return semaphore_->Wait(1000000); }
420
421 void Reset() { was_sample_stack_called_ = false; }
422
423 bool WasSampleStackCalled() { return was_sample_stack_called_; }
424
425 private:
426 v8::internal::Semaphore* semaphore_;
427 bool was_sample_stack_called_;
428};
429
430
431} // namespace
432
433TEST(ProfMultipleThreads) {
434 LoopingJsThread jsThread;
435 jsThread.Start();
436 LoopingNonJsThread nonJsThread;
437 nonJsThread.Start();
438
439 TestSampler sampler;
440 sampler.Start();
441 CHECK(!sampler.WasSampleStackCalled());
442 jsThread.WaitForRunning();
443 jsThread.SendSigProf();
444 CHECK(sampler.WaitForTick());
445 CHECK(sampler.WasSampleStackCalled());
446 sampler.Reset();
447 CHECK(!sampler.WasSampleStackCalled());
448 nonJsThread.WaitForRunning();
449 nonJsThread.SendSigProf();
Ben Murdochf87a2032010-10-22 12:50:53 +0100450 CHECK(!sampler.WaitForTick());
Steve Blocka7e24c12009-10-30 11:49:00 +0000451 CHECK(!sampler.WasSampleStackCalled());
452 sampler.Stop();
453
454 jsThread.Stop();
455 nonJsThread.Stop();
456 jsThread.Join();
457 nonJsThread.Join();
458}
459
460#endif // __linux__
461
462
Steve Block3ce2e202009-11-05 08:53:23 +0000463// Test for issue http://crbug.com/23768 in Chromium.
464// Heap can contain scripts with already disposed external sources.
465// We need to verify that LogCompiledFunctions doesn't crash on them.
466namespace {
467
468class SimpleExternalString : public v8::String::ExternalStringResource {
469 public:
470 explicit SimpleExternalString(const char* source)
Steve Blockd0582a62009-12-15 09:54:21 +0000471 : utf_source_(StrLength(source)) {
Steve Block3ce2e202009-11-05 08:53:23 +0000472 for (int i = 0; i < utf_source_.length(); ++i)
473 utf_source_[i] = source[i];
474 }
475 virtual ~SimpleExternalString() {}
476 virtual size_t length() const { return utf_source_.length(); }
477 virtual const uint16_t* data() const { return utf_source_.start(); }
478 private:
479 i::ScopedVector<uint16_t> utf_source_;
480};
481
482} // namespace
483
484TEST(Issue23768) {
485 v8::HandleScope scope;
486 v8::Handle<v8::Context> env = v8::Context::New();
487 env->Enter();
488
489 SimpleExternalString source_ext_str("(function ext() {})();");
490 v8::Local<v8::String> source = v8::String::NewExternal(&source_ext_str);
491 // Script needs to have a name in order to trigger InitLineEnds execution.
492 v8::Handle<v8::String> origin = v8::String::New("issue-23768-test");
493 v8::Handle<v8::Script> evil_script = v8::Script::Compile(source, origin);
494 CHECK(!evil_script.IsEmpty());
495 CHECK(!evil_script->Run().IsEmpty());
496 i::Handle<i::ExternalTwoByteString> i_source(
497 i::ExternalTwoByteString::cast(*v8::Utils::OpenHandle(*source)));
498 // This situation can happen if source was an external string disposed
499 // by its owner.
500 i_source->set_resource(NULL);
501
502 // Must not crash.
503 i::Logger::LogCompiledFunctions();
504}
505
506
Steve Blockd0582a62009-12-15 09:54:21 +0000507static v8::Handle<v8::Value> ObjMethod1(const v8::Arguments& args) {
508 return v8::Handle<v8::Value>();
509}
510
511TEST(LogCallbacks) {
Steve Block6ded16b2010-05-10 14:33:55 +0100512 ScopedLoggerInitializer initialize_logger(false);
Andrei Popescu402d9372010-02-26 13:31:12 +0000513 LogBufferMatcher matcher;
Steve Blockd0582a62009-12-15 09:54:21 +0000514
515 v8::Persistent<v8::FunctionTemplate> obj =
516 v8::Persistent<v8::FunctionTemplate>::New(v8::FunctionTemplate::New());
517 obj->SetClassName(v8::String::New("Obj"));
518 v8::Handle<v8::ObjectTemplate> proto = obj->PrototypeTemplate();
519 v8::Local<v8::Signature> signature = v8::Signature::New(obj);
520 proto->Set(v8::String::New("method1"),
521 v8::FunctionTemplate::New(ObjMethod1,
522 v8::Handle<v8::Value>(),
523 signature),
524 static_cast<v8::PropertyAttribute>(v8::DontDelete));
525
Andrei Popescu402d9372010-02-26 13:31:12 +0000526 initialize_logger.env()->Global()->Set(v8_str("Obj"), obj->GetFunction());
Steve Blockd0582a62009-12-15 09:54:21 +0000527 CompileAndRunScript("Obj.prototype.method1.toString();");
528
529 i::Logger::LogCompiledFunctions();
Andrei Popescu402d9372010-02-26 13:31:12 +0000530 CHECK_GT(matcher.GetNextChunk(), 0);
Steve Blockd0582a62009-12-15 09:54:21 +0000531
532 const char* callback_rec = "code-creation,Callback,";
Andrei Popescu402d9372010-02-26 13:31:12 +0000533 char* pos = const_cast<char*>(matcher.Find(callback_rec));
Steve Blockd0582a62009-12-15 09:54:21 +0000534 CHECK_NE(NULL, pos);
535 pos += strlen(callback_rec);
536 EmbeddedVector<char, 100> ref_data;
537 i::OS::SNPrintF(ref_data,
538 "0x%" V8PRIxPTR ",1,\"method1\"", ObjMethod1);
539 *(pos + strlen(ref_data.start())) = '\0';
540 CHECK_EQ(ref_data.start(), pos);
541
542 obj.Dispose();
Steve Blockd0582a62009-12-15 09:54:21 +0000543}
544
545
546static v8::Handle<v8::Value> Prop1Getter(v8::Local<v8::String> property,
547 const v8::AccessorInfo& info) {
548 return v8::Handle<v8::Value>();
549}
550
551static void Prop1Setter(v8::Local<v8::String> property,
552 v8::Local<v8::Value> value,
553 const v8::AccessorInfo& info) {
554}
555
556static v8::Handle<v8::Value> Prop2Getter(v8::Local<v8::String> property,
557 const v8::AccessorInfo& info) {
558 return v8::Handle<v8::Value>();
559}
560
561TEST(LogAccessorCallbacks) {
Steve Block6ded16b2010-05-10 14:33:55 +0100562 ScopedLoggerInitializer initialize_logger(false);
Andrei Popescu402d9372010-02-26 13:31:12 +0000563 LogBufferMatcher matcher;
Steve Blockd0582a62009-12-15 09:54:21 +0000564
565 v8::Persistent<v8::FunctionTemplate> obj =
566 v8::Persistent<v8::FunctionTemplate>::New(v8::FunctionTemplate::New());
567 obj->SetClassName(v8::String::New("Obj"));
568 v8::Handle<v8::ObjectTemplate> inst = obj->InstanceTemplate();
569 inst->SetAccessor(v8::String::New("prop1"), Prop1Getter, Prop1Setter);
570 inst->SetAccessor(v8::String::New("prop2"), Prop2Getter);
571
572 i::Logger::LogAccessorCallbacks();
Andrei Popescu402d9372010-02-26 13:31:12 +0000573 CHECK_GT(matcher.GetNextChunk(), 0);
574 matcher.PrintBuffer();
Steve Blockd0582a62009-12-15 09:54:21 +0000575
576 EmbeddedVector<char, 100> prop1_getter_record;
577 i::OS::SNPrintF(prop1_getter_record,
578 "code-creation,Callback,0x%" V8PRIxPTR ",1,\"get prop1\"",
579 Prop1Getter);
Andrei Popescu402d9372010-02-26 13:31:12 +0000580 CHECK_NE(NULL, matcher.Find(prop1_getter_record));
Steve Blockd0582a62009-12-15 09:54:21 +0000581 EmbeddedVector<char, 100> prop1_setter_record;
582 i::OS::SNPrintF(prop1_setter_record,
583 "code-creation,Callback,0x%" V8PRIxPTR ",1,\"set prop1\"",
584 Prop1Setter);
Andrei Popescu402d9372010-02-26 13:31:12 +0000585 CHECK_NE(NULL, matcher.Find(prop1_setter_record));
Steve Blockd0582a62009-12-15 09:54:21 +0000586 EmbeddedVector<char, 100> prop2_getter_record;
587 i::OS::SNPrintF(prop2_getter_record,
588 "code-creation,Callback,0x%" V8PRIxPTR ",1,\"get prop2\"",
589 Prop2Getter);
Andrei Popescu402d9372010-02-26 13:31:12 +0000590 CHECK_NE(NULL, matcher.Find(prop2_getter_record));
Steve Blockd0582a62009-12-15 09:54:21 +0000591
592 obj.Dispose();
Andrei Popescu402d9372010-02-26 13:31:12 +0000593}
Steve Blockd0582a62009-12-15 09:54:21 +0000594
Andrei Popescu402d9372010-02-26 13:31:12 +0000595
596TEST(LogTags) {
Steve Block6ded16b2010-05-10 14:33:55 +0100597 ScopedLoggerInitializer initialize_logger(false);
Andrei Popescu402d9372010-02-26 13:31:12 +0000598 LogBufferMatcher matcher;
599
600 const char* open_tag = "open-tag,";
601 const char* close_tag = "close-tag,";
602
603 // Check compatibility with the old style behavior.
604 CHECK_EQ(v8::PROFILER_MODULE_NONE, Logger::GetActiveProfilerModules());
605 Logger::ResumeProfiler(v8::PROFILER_MODULE_CPU, 0);
606 CHECK_EQ(v8::PROFILER_MODULE_CPU, Logger::GetActiveProfilerModules());
607 Logger::PauseProfiler(v8::PROFILER_MODULE_CPU, 0);
608 CHECK_EQ(v8::PROFILER_MODULE_NONE, Logger::GetActiveProfilerModules());
609 CHECK_EQ(NULL, matcher.Find(open_tag));
610 CHECK_EQ(NULL, matcher.Find(close_tag));
611
612 const char* open_tag1 = "open-tag,1\n";
613 const char* close_tag1 = "close-tag,1\n";
614
615 // Check non-nested tag case.
616 CHECK_EQ(v8::PROFILER_MODULE_NONE, Logger::GetActiveProfilerModules());
617 Logger::ResumeProfiler(v8::PROFILER_MODULE_CPU, 1);
618 CHECK_EQ(v8::PROFILER_MODULE_CPU, Logger::GetActiveProfilerModules());
619 Logger::PauseProfiler(v8::PROFILER_MODULE_CPU, 1);
620 CHECK_EQ(v8::PROFILER_MODULE_NONE, Logger::GetActiveProfilerModules());
621 CHECK_GT(matcher.GetNextChunk(), 0);
622 CHECK(matcher.IsInSequence(open_tag1, close_tag1));
623
624 const char* open_tag2 = "open-tag,2\n";
625 const char* close_tag2 = "close-tag,2\n";
626
627 // Check nested tags case.
628 CHECK_EQ(v8::PROFILER_MODULE_NONE, Logger::GetActiveProfilerModules());
629 Logger::ResumeProfiler(v8::PROFILER_MODULE_CPU, 1);
630 CHECK_EQ(v8::PROFILER_MODULE_CPU, Logger::GetActiveProfilerModules());
631 Logger::ResumeProfiler(v8::PROFILER_MODULE_CPU, 2);
632 CHECK_EQ(v8::PROFILER_MODULE_CPU, Logger::GetActiveProfilerModules());
633 Logger::PauseProfiler(v8::PROFILER_MODULE_CPU, 2);
634 CHECK_EQ(v8::PROFILER_MODULE_CPU, Logger::GetActiveProfilerModules());
635 Logger::PauseProfiler(v8::PROFILER_MODULE_CPU, 1);
636 CHECK_EQ(v8::PROFILER_MODULE_NONE, Logger::GetActiveProfilerModules());
637 CHECK_GT(matcher.GetNextChunk(), 0);
638 // open_tag1 < open_tag2 < close_tag2 < close_tag1
639 CHECK(matcher.IsInSequence(open_tag1, open_tag2));
640 CHECK(matcher.IsInSequence(open_tag2, close_tag2));
641 CHECK(matcher.IsInSequence(close_tag2, close_tag1));
642
643 // Check overlapped tags case.
644 CHECK_EQ(v8::PROFILER_MODULE_NONE, Logger::GetActiveProfilerModules());
645 Logger::ResumeProfiler(v8::PROFILER_MODULE_CPU, 1);
646 CHECK_EQ(v8::PROFILER_MODULE_CPU, Logger::GetActiveProfilerModules());
647 Logger::ResumeProfiler(v8::PROFILER_MODULE_CPU, 2);
648 CHECK_EQ(v8::PROFILER_MODULE_CPU, Logger::GetActiveProfilerModules());
649 Logger::PauseProfiler(v8::PROFILER_MODULE_CPU, 1);
650 CHECK_EQ(v8::PROFILER_MODULE_CPU, Logger::GetActiveProfilerModules());
651 Logger::PauseProfiler(v8::PROFILER_MODULE_CPU, 2);
652 CHECK_EQ(v8::PROFILER_MODULE_NONE, Logger::GetActiveProfilerModules());
653 CHECK_GT(matcher.GetNextChunk(), 0);
654 // open_tag1 < open_tag2 < close_tag1 < close_tag2
655 CHECK(matcher.IsInSequence(open_tag1, open_tag2));
656 CHECK(matcher.IsInSequence(open_tag2, close_tag1));
657 CHECK(matcher.IsInSequence(close_tag1, close_tag2));
658
659 const char* open_tag3 = "open-tag,3\n";
660 const char* close_tag3 = "close-tag,3\n";
661
662 // Check pausing overflow case.
663 CHECK_EQ(v8::PROFILER_MODULE_NONE, Logger::GetActiveProfilerModules());
664 Logger::ResumeProfiler(v8::PROFILER_MODULE_CPU, 1);
665 CHECK_EQ(v8::PROFILER_MODULE_CPU, Logger::GetActiveProfilerModules());
666 Logger::ResumeProfiler(v8::PROFILER_MODULE_CPU, 2);
667 CHECK_EQ(v8::PROFILER_MODULE_CPU, Logger::GetActiveProfilerModules());
668 Logger::PauseProfiler(v8::PROFILER_MODULE_CPU, 2);
669 CHECK_EQ(v8::PROFILER_MODULE_CPU, Logger::GetActiveProfilerModules());
670 Logger::PauseProfiler(v8::PROFILER_MODULE_CPU, 1);
671 CHECK_EQ(v8::PROFILER_MODULE_NONE, Logger::GetActiveProfilerModules());
672 Logger::PauseProfiler(v8::PROFILER_MODULE_CPU, 3);
673 CHECK_EQ(v8::PROFILER_MODULE_NONE, Logger::GetActiveProfilerModules());
674 Logger::ResumeProfiler(v8::PROFILER_MODULE_CPU, 3);
675 CHECK_EQ(v8::PROFILER_MODULE_NONE, Logger::GetActiveProfilerModules());
676 // Must be no tags, because logging must be disabled.
677 CHECK_EQ(NULL, matcher.Find(open_tag3));
678 CHECK_EQ(NULL, matcher.Find(close_tag3));
Steve Blockd0582a62009-12-15 09:54:21 +0000679}
680
681
Steve Block6ded16b2010-05-10 14:33:55 +0100682TEST(IsLoggingPreserved) {
683 ScopedLoggerInitializer initialize_logger(false);
684
685 CHECK(Logger::is_logging());
686 Logger::ResumeProfiler(v8::PROFILER_MODULE_CPU, 1);
687 CHECK(Logger::is_logging());
688 Logger::PauseProfiler(v8::PROFILER_MODULE_CPU, 1);
689 CHECK(Logger::is_logging());
690
691 CHECK(Logger::is_logging());
692 Logger::ResumeProfiler(
693 v8::PROFILER_MODULE_HEAP_STATS | v8::PROFILER_MODULE_JS_CONSTRUCTORS, 1);
694 CHECK(Logger::is_logging());
695 Logger::PauseProfiler(
696 v8::PROFILER_MODULE_HEAP_STATS | v8::PROFILER_MODULE_JS_CONSTRUCTORS, 1);
697 CHECK(Logger::is_logging());
698
699 CHECK(Logger::is_logging());
700 Logger::ResumeProfiler(
701 v8::PROFILER_MODULE_CPU |
702 v8::PROFILER_MODULE_HEAP_STATS | v8::PROFILER_MODULE_JS_CONSTRUCTORS, 1);
703 CHECK(Logger::is_logging());
704 Logger::PauseProfiler(
705 v8::PROFILER_MODULE_CPU |
706 v8::PROFILER_MODULE_HEAP_STATS | v8::PROFILER_MODULE_JS_CONSTRUCTORS, 1);
707 CHECK(Logger::is_logging());
708}
709
710
Steve Blocka7e24c12009-10-30 11:49:00 +0000711static inline bool IsStringEqualTo(const char* r, const char* s) {
712 return strncmp(r, s, strlen(r)) == 0;
713}
714
715
716static bool Consume(const char* str, char** buf) {
717 if (IsStringEqualTo(str, *buf)) {
718 *buf += strlen(str);
719 return true;
720 }
721 return false;
722}
723
724
725namespace {
726
727// A code entity is a pointer to a position of code-creation event in buffer log
728// offset to a point where entity size begins, i.e.: '255,"func"\n'. This makes
729// comparing code entities pretty easy.
730typedef char* CodeEntityInfo;
731
732class Interval {
733 public:
734 Interval()
735 : min_addr_(reinterpret_cast<Address>(-1)),
736 max_addr_(reinterpret_cast<Address>(0)), next_(NULL) {}
737
738 ~Interval() { delete next_; }
739
740 size_t Length() {
741 size_t result = max_addr_ - min_addr_ + 1;
742 if (next_ != NULL) result += next_->Length();
743 return result;
744 }
745
746 void CloneFrom(Interval* src) {
747 while (src != NULL) {
748 RegisterAddress(src->min_addr_);
749 RegisterAddress(src->max_addr_);
750 src = src->next_;
751 }
752 }
753
754 bool Contains(Address addr) {
755 if (min_addr_ <= addr && addr <= max_addr_) {
756 return true;
757 }
758 if (next_ != NULL) {
759 return next_->Contains(addr);
760 } else {
761 return false;
762 }
763 }
764
765 size_t GetIndex(Address addr) {
766 if (min_addr_ <= addr && addr <= max_addr_) {
767 return addr - min_addr_;
768 }
769 CHECK_NE(NULL, next_);
770 return (max_addr_ - min_addr_ + 1) + next_->GetIndex(addr);
771 }
772
773 Address GetMinAddr() {
774 return next_ == NULL ? min_addr_ : i::Min(min_addr_, next_->GetMinAddr());
775 }
776
777 Address GetMaxAddr() {
778 return next_ == NULL ? max_addr_ : i::Max(max_addr_, next_->GetMaxAddr());
779 }
780
781 void RegisterAddress(Address addr) {
782 if (min_addr_ == reinterpret_cast<Address>(-1)
783 || (size_t)(addr > min_addr_ ?
784 addr - min_addr_ : min_addr_ - addr) < MAX_DELTA) {
785 if (addr < min_addr_) min_addr_ = addr;
786 if (addr > max_addr_) max_addr_ = addr;
787 } else {
788 if (next_ == NULL) next_ = new Interval();
789 next_->RegisterAddress(addr);
790 }
791 }
792
793 Address raw_min_addr() { return min_addr_; }
794
795 Address raw_max_addr() { return max_addr_; }
796
797 Interval* get_next() { return next_; }
798
799 private:
800 static const size_t MAX_DELTA = 0x100000;
801 Address min_addr_;
802 Address max_addr_;
803 Interval* next_;
804};
805
806
807// A structure used to return log parsing results.
808class ParseLogResult {
809 public:
810 ParseLogResult()
811 : entities_map(NULL), entities(NULL),
812 max_entities(0) {}
813
814 ~ParseLogResult() {
815 i::DeleteArray(entities_map);
816 i::DeleteArray(entities);
817 }
818
819 void AllocateEntities() {
820 // Make sure that the test doesn't operate on a bogus log.
821 CHECK_GT(max_entities, 0);
822 CHECK_GT(bounds.GetMinAddr(), 0);
823 CHECK_GT(bounds.GetMaxAddr(), bounds.GetMinAddr());
824
825 entities = i::NewArray<CodeEntityInfo>(max_entities);
826 for (int i = 0; i < max_entities; ++i) {
827 entities[i] = NULL;
828 }
829 const size_t map_length = bounds.Length();
Steve Blockd0582a62009-12-15 09:54:21 +0000830 entities_map = i::NewArray<int>(static_cast<int>(map_length));
Steve Blocka7e24c12009-10-30 11:49:00 +0000831 for (size_t i = 0; i < map_length; ++i) {
832 entities_map[i] = -1;
833 }
834 }
835
836 bool HasIndexForAddress(Address addr) {
837 return bounds.Contains(addr);
838 }
839
840 size_t GetIndexForAddress(Address addr) {
841 CHECK(HasIndexForAddress(addr));
842 return bounds.GetIndex(addr);
843 }
844
845 CodeEntityInfo GetEntity(Address addr) {
846 if (HasIndexForAddress(addr)) {
847 size_t idx = GetIndexForAddress(addr);
848 int item = entities_map[idx];
849 return item != -1 ? entities[item] : NULL;
850 }
851 return NULL;
852 }
853
854 void ParseAddress(char* start) {
855 Address addr =
856 reinterpret_cast<Address>(strtoul(start, NULL, 16)); // NOLINT
857 bounds.RegisterAddress(addr);
858 }
859
860 Address ConsumeAddress(char** start) {
861 char* end_ptr;
862 Address addr =
863 reinterpret_cast<Address>(strtoul(*start, &end_ptr, 16)); // NOLINT
864 CHECK(HasIndexForAddress(addr));
865 *start = end_ptr;
866 return addr;
867 }
868
869 Interval bounds;
870 // Memory map of entities start addresses.
871 int* entities_map;
872 // An array of code entities.
873 CodeEntityInfo* entities;
874 // Maximal entities count. Actual entities count can be lower,
875 // empty entity slots are pointing to NULL.
876 int max_entities;
877};
878
879} // namespace
880
881
882typedef void (*ParserBlock)(char* start, char* end, ParseLogResult* result);
883
884static void ParserCycle(
885 char* start, char* end, ParseLogResult* result,
886 ParserBlock block_creation, ParserBlock block_delete,
887 ParserBlock block_move) {
888
889 const char* code_creation = "code-creation,";
890 const char* code_delete = "code-delete,";
891 const char* code_move = "code-move,";
892
893 const char* lazy_compile = "LazyCompile,";
894 const char* script = "Script,";
895 const char* function = "Function,";
896
897 while (start < end) {
898 if (Consume(code_creation, &start)) {
899 if (Consume(lazy_compile, &start)
900 || Consume(script, &start)
901 || Consume(function, &start)) {
902 block_creation(start, end, result);
903 }
904 } else if (Consume(code_delete, &start)) {
905 block_delete(start, end, result);
906 } else if (Consume(code_move, &start)) {
907 block_move(start, end, result);
908 }
909 while (start < end && *start != '\n') ++start;
910 ++start;
911 }
912}
913
914
915static void Pass1CodeCreation(char* start, char* end, ParseLogResult* result) {
916 result->ParseAddress(start);
917 ++result->max_entities;
918}
919
920
921static void Pass1CodeDelete(char* start, char* end, ParseLogResult* result) {
922 result->ParseAddress(start);
923}
924
925
926static void Pass1CodeMove(char* start, char* end, ParseLogResult* result) {
927 result->ParseAddress(start);
928 // Skip old address.
929 while (start < end && *start != ',') ++start;
930 CHECK_GT(end, start);
931 ++start; // Skip ','.
932 result->ParseAddress(start);
933}
934
935
936static void Pass2CodeCreation(char* start, char* end, ParseLogResult* result) {
937 Address addr = result->ConsumeAddress(&start);
938 CHECK_GT(end, start);
939 ++start; // Skip ','.
940
941 size_t idx = result->GetIndexForAddress(addr);
942 result->entities_map[idx] = -1;
943 for (int i = 0; i < result->max_entities; ++i) {
944 // Find an empty slot and fill it.
945 if (result->entities[i] == NULL) {
946 result->entities[i] = start;
947 result->entities_map[idx] = i;
948 break;
949 }
950 }
951 // Make sure that a slot was found.
952 CHECK_GE(result->entities_map[idx], 0);
953}
954
955
956static void Pass2CodeDelete(char* start, char* end, ParseLogResult* result) {
957 Address addr = result->ConsumeAddress(&start);
958 size_t idx = result->GetIndexForAddress(addr);
959 // There can be code deletes that are not related to JS code.
960 if (result->entities_map[idx] >= 0) {
961 result->entities[result->entities_map[idx]] = NULL;
962 result->entities_map[idx] = -1;
963 }
964}
965
966
967static void Pass2CodeMove(char* start, char* end, ParseLogResult* result) {
968 Address from_addr = result->ConsumeAddress(&start);
969 CHECK_GT(end, start);
970 ++start; // Skip ','.
971 Address to_addr = result->ConsumeAddress(&start);
972 CHECK_GT(end, start);
973
974 size_t from_idx = result->GetIndexForAddress(from_addr);
975 size_t to_idx = result->GetIndexForAddress(to_addr);
976 // There can be code moves that are not related to JS code.
977 if (from_idx != to_idx && result->entities_map[from_idx] >= 0) {
978 CHECK_EQ(-1, result->entities_map[to_idx]);
979 result->entities_map[to_idx] = result->entities_map[from_idx];
980 result->entities_map[from_idx] = -1;
981 };
982}
983
984
985static void ParseLog(char* start, char* end, ParseLogResult* result) {
986 // Pass 1: Calculate boundaries of addresses and entities count.
987 ParserCycle(start, end, result,
988 Pass1CodeCreation, Pass1CodeDelete, Pass1CodeMove);
989
990 printf("min_addr: %p, max_addr: %p, entities: %d\n",
991 result->bounds.GetMinAddr(), result->bounds.GetMaxAddr(),
992 result->max_entities);
993
994 result->AllocateEntities();
995
996 // Pass 2: Fill in code entries data.
997 ParserCycle(start, end, result,
998 Pass2CodeCreation, Pass2CodeDelete, Pass2CodeMove);
999}
1000
1001
1002static inline void PrintCodeEntityInfo(CodeEntityInfo entity) {
1003 const int max_len = 50;
1004 if (entity != NULL) {
1005 char* eol = strchr(entity, '\n');
Steve Blockd0582a62009-12-15 09:54:21 +00001006 int len = static_cast<int>(eol - entity);
Steve Blocka7e24c12009-10-30 11:49:00 +00001007 len = len <= max_len ? len : max_len;
1008 printf("%-*.*s ", max_len, len, entity);
1009 } else {
1010 printf("%*s", max_len + 1, "");
1011 }
1012}
1013
1014
1015static void PrintCodeEntitiesInfo(
1016 bool is_equal, Address addr,
1017 CodeEntityInfo l_entity, CodeEntityInfo r_entity) {
1018 printf("%c %p ", is_equal ? ' ' : '*', addr);
1019 PrintCodeEntityInfo(l_entity);
1020 PrintCodeEntityInfo(r_entity);
1021 printf("\n");
1022}
1023
1024
1025static inline int StrChrLen(const char* s, char c) {
Steve Blockd0582a62009-12-15 09:54:21 +00001026 return static_cast<int>(strchr(s, c) - s);
Steve Blocka7e24c12009-10-30 11:49:00 +00001027}
1028
1029
1030static bool AreFuncSizesEqual(CodeEntityInfo ref_s, CodeEntityInfo new_s) {
1031 int ref_len = StrChrLen(ref_s, ',');
1032 int new_len = StrChrLen(new_s, ',');
1033 return ref_len == new_len && strncmp(ref_s, new_s, ref_len) == 0;
1034}
1035
1036
1037static bool AreFuncNamesEqual(CodeEntityInfo ref_s, CodeEntityInfo new_s) {
1038 // Skip size.
1039 ref_s = strchr(ref_s, ',') + 1;
1040 new_s = strchr(new_s, ',') + 1;
1041 int ref_len = StrChrLen(ref_s, '\n');
1042 int new_len = StrChrLen(new_s, '\n');
1043 // If reference is anonymous (""), it's OK to have anything in new.
1044 if (ref_len == 2) return true;
1045 // A special case for ErrorPrototype. Haven't yet figured out why they
1046 // are different.
1047 const char* error_prototype = "\"ErrorPrototype";
1048 if (IsStringEqualTo(error_prototype, ref_s)
1049 && IsStringEqualTo(error_prototype, new_s)) {
1050 return true;
1051 }
1052 // Built-in objects have problems too.
1053 const char* built_ins[] = {
1054 "\"Boolean\"", "\"Function\"", "\"Number\"",
1055 "\"Object\"", "\"Script\"", "\"String\""
1056 };
1057 for (size_t i = 0; i < sizeof(built_ins) / sizeof(*built_ins); ++i) {
1058 if (IsStringEqualTo(built_ins[i], new_s)) {
1059 return true;
1060 }
1061 }
1062 return ref_len == new_len && strncmp(ref_s, new_s, ref_len) == 0;
1063}
1064
1065
1066static bool AreEntitiesEqual(CodeEntityInfo ref_e, CodeEntityInfo new_e) {
1067 if (ref_e == NULL && new_e != NULL) return true;
1068 if (ref_e != NULL && new_e != NULL) {
1069 return AreFuncSizesEqual(ref_e, new_e) && AreFuncNamesEqual(ref_e, new_e);
1070 }
1071 if (ref_e != NULL && new_e == NULL) {
1072 // args_count entities (argument adapters) are not found by heap traversal,
1073 // but they are not needed because they doesn't contain any code.
1074 ref_e = strchr(ref_e, ',') + 1;
1075 const char* args_count = "\"args_count:";
1076 return IsStringEqualTo(args_count, ref_e);
1077 }
1078 return false;
1079}
1080
1081
1082// Test that logging of code create / move / delete events
1083// is equivalent to traversal of a resulting heap.
1084TEST(EquivalenceOfLoggingAndTraversal) {
1085 // This test needs to be run on a "clean" V8 to ensure that snapshot log
1086 // is loaded. This is always true when running using tools/test.py because
1087 // it launches a new cctest instance for every test. To be sure that launching
1088 // cctest manually also works, please be sure that no tests below
1089 // are using V8.
1090 //
1091 // P.S. No, V8 can't be re-initialized after disposal, see include/v8.h.
1092 CHECK(!i::V8::IsRunning());
1093
1094 i::FLAG_logfile = "*";
1095 i::FLAG_log = true;
1096 i::FLAG_log_code = true;
1097
1098 // Make sure objects move.
1099 bool saved_always_compact = i::FLAG_always_compact;
1100 if (!i::FLAG_never_compact) {
1101 i::FLAG_always_compact = true;
1102 }
1103
1104 v8::HandleScope scope;
1105 v8::Handle<v8::Value> global_object = v8::Handle<v8::Value>();
1106 v8::Handle<v8::Context> env = v8::Context::New(
1107 0, v8::Handle<v8::ObjectTemplate>(), global_object);
1108 env->Enter();
1109
1110 // Compile and run a function that creates other functions.
1111 CompileAndRunScript(
1112 "(function f(obj) {\n"
1113 " obj.test =\n"
1114 " (function a(j) { return function b() { return j; } })(100);\n"
1115 "})(this);");
1116 i::Heap::CollectAllGarbage(false);
1117
1118 EmbeddedVector<char, 204800> buffer;
1119 int log_size;
1120 ParseLogResult ref_result;
1121
1122 // Retrieve the log.
1123 {
1124 // Make sure that no GCs occur prior to LogCompiledFunctions call.
1125 i::AssertNoAllocation no_alloc;
1126
1127 log_size = GetLogLines(0, &buffer);
1128 CHECK_GT(log_size, 0);
1129 CHECK_GT(buffer.length(), log_size);
1130
1131 // Fill a map of compiled code objects.
1132 ParseLog(buffer.start(), buffer.start() + log_size, &ref_result);
1133 }
1134
1135 // Iterate heap to find compiled functions, will write to log.
1136 i::Logger::LogCompiledFunctions();
1137 char* new_log_start = buffer.start() + log_size;
1138 const int new_log_size = Logger::GetLogLines(
1139 log_size, new_log_start, buffer.length() - log_size);
1140 CHECK_GT(new_log_size, 0);
1141 CHECK_GT(buffer.length(), log_size + new_log_size);
1142
1143 // Fill an equivalent map of compiled code objects.
1144 ParseLogResult new_result;
1145 ParseLog(new_log_start, new_log_start + new_log_size, &new_result);
1146
1147 // Test their actual equivalence.
1148 Interval combined;
1149 combined.CloneFrom(&ref_result.bounds);
1150 combined.CloneFrom(&new_result.bounds);
1151 Interval* iter = &combined;
1152 bool results_equal = true;
1153
1154 while (iter != NULL) {
1155 for (Address addr = iter->raw_min_addr();
1156 addr <= iter->raw_max_addr(); ++addr) {
1157 CodeEntityInfo ref_entity = ref_result.GetEntity(addr);
1158 CodeEntityInfo new_entity = new_result.GetEntity(addr);
1159 if (ref_entity != NULL || new_entity != NULL) {
1160 const bool equal = AreEntitiesEqual(ref_entity, new_entity);
1161 if (!equal) results_equal = false;
1162 PrintCodeEntitiesInfo(equal, addr, ref_entity, new_entity);
1163 }
1164 }
1165 iter = iter->get_next();
1166 }
1167 // Make sure that all log data is written prior crash due to CHECK failure.
1168 fflush(stdout);
1169 CHECK(results_equal);
1170
1171 env->Exit();
1172 Logger::TearDown();
1173 i::FLAG_always_compact = saved_always_compact;
1174}
1175
1176#endif // ENABLE_LOGGING_AND_PROFILING