Implement tagging of profiler log event blocks.
authormikhail.naganov@gmail.com <mikhail.naganov@gmail.com@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Wed, 17 Feb 2010 13:23:46 +0000 (13:23 +0000)
committermikhail.naganov@gmail.com <mikhail.naganov@gmail.com@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Wed, 17 Feb 2010 13:23:46 +0000 (13:23 +0000)
This change allows to associate integer tags with blocks of profiler
log events, and repeat calls to 'ResumeProfiler' / 'PauseProfiler' in
order to establsh nested (not necessary properly nested) blocks. By
supporting this, we will be able to match WebInspector's CPU profiler
abilities in DevTools.

I also refactored some testing code.

Review URL: http://codereview.chromium.org/619004

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@3889 ce2b1a6d-e550-0410-aec6-3dcde31c8c00

include/v8.h
src/api.cc
src/checks.h
src/debug-delay.js
src/log.cc
src/log.h
src/runtime.cc
src/runtime.h
test/cctest/test-log.cc

index 96dc46972b75496289bf6e8a0246debd478d3a1e..13f819170376fe7c3c5bf0c4c5e7fd3c4ee219ae 100644 (file)
@@ -2349,22 +2349,30 @@ class V8EXPORT V8 {
   static bool IsProfilerPaused();
 
   /**
-   * Resumes specified profiler modules.
+   * Resumes specified profiler modules. Can be called several times to
+   * mark the opening of a profiler events block with the given tag.
+   *
    * "ResumeProfiler" is equivalent to "ResumeProfilerEx(PROFILER_MODULE_CPU)".
    * See ProfilerModules enum.
    *
    * \param flags Flags specifying profiler modules.
+   * \param tag Profile tag.
    */
-  static void ResumeProfilerEx(int flags);
+  static void ResumeProfilerEx(int flags, int tag = 0);
 
   /**
-   * Pauses specified profiler modules.
+   * Pauses specified profiler modules. Each call to "PauseProfilerEx" closes
+   * a block of profiler events opened by a call to "ResumeProfilerEx" with the
+   * same tag value. There is no need for blocks to be properly nested.
+   * The profiler is paused when the last opened block is closed.
+   *
    * "PauseProfiler" is equivalent to "PauseProfilerEx(PROFILER_MODULE_CPU)".
    * See ProfilerModules enum.
    *
    * \param flags Flags specifying profiler modules.
+   * \param tag Profile tag.
    */
-  static void PauseProfilerEx(int flags);
+  static void PauseProfilerEx(int flags, int tag = 0);
 
   /**
    * Returns active (resumed) profiler modules.
index 0df24e8abfc95558fee4ddd1e3eb30fdc2cd699b..f75355224d490735794cd181eb4f35c83cd673ea 100644 (file)
@@ -3469,14 +3469,14 @@ void V8::SetGlobalGCEpilogueCallback(GCCallback callback) {
 
 void V8::PauseProfiler() {
 #ifdef ENABLE_LOGGING_AND_PROFILING
-  i::Logger::PauseProfiler(PROFILER_MODULE_CPU);
+  PauseProfilerEx(PROFILER_MODULE_CPU);
 #endif
 }
 
 
 void V8::ResumeProfiler() {
 #ifdef ENABLE_LOGGING_AND_PROFILING
-  i::Logger::ResumeProfiler(PROFILER_MODULE_CPU);
+  ResumeProfilerEx(PROFILER_MODULE_CPU);
 #endif
 }
 
@@ -3490,7 +3490,7 @@ bool V8::IsProfilerPaused() {
 }
 
 
-void V8::ResumeProfilerEx(int flags) {
+void V8::ResumeProfilerEx(int flags, int tag) {
 #ifdef ENABLE_LOGGING_AND_PROFILING
   if (flags & PROFILER_MODULE_HEAP_SNAPSHOT) {
     // Snapshot mode: resume modules, perform GC, then pause only
@@ -3500,19 +3500,19 @@ void V8::ResumeProfilerEx(int flags) {
     // Reset snapshot flag and CPU module flags.
     flags &= ~(PROFILER_MODULE_HEAP_SNAPSHOT | PROFILER_MODULE_CPU);
     const int current_flags = i::Logger::GetActiveProfilerModules();
-    i::Logger::ResumeProfiler(flags);
+    i::Logger::ResumeProfiler(flags, tag);
     i::Heap::CollectAllGarbage(false);
-    i::Logger::PauseProfiler(~current_flags & flags);
+    i::Logger::PauseProfiler(~current_flags & flags, tag);
   } else {
-    i::Logger::ResumeProfiler(flags);
+    i::Logger::ResumeProfiler(flags, tag);
   }
 #endif
 }
 
 
-void V8::PauseProfilerEx(int flags) {
+void V8::PauseProfilerEx(int flags, int tag) {
 #ifdef ENABLE_LOGGING_AND_PROFILING
-  i::Logger::PauseProfiler(flags);
+  i::Logger::PauseProfiler(flags, tag);
 #endif
 }
 
index 3b0c85135475f3d028694ad5d962d004d88218c0..eeb748b4a80d875e16234a4a20b14575b33a7be9 100644 (file)
@@ -125,7 +125,9 @@ static inline void CheckEqualsHelper(const char* file,
                                      const char* expected,
                                      const char* value_source,
                                      const char* value) {
-  if (strcmp(expected, value) != 0) {
+  if ((expected == NULL && value != NULL) ||
+      (expected != NULL && value == NULL) ||
+      (expected != NULL && value != NULL && strcmp(expected, value) != 0)) {
     V8_Fatal(file, line,
              "CHECK_EQ(%s, %s) failed\n#   Expected: %s\n#   Found: %s",
              expected_source, value_source, expected, value);
index 14d8c8830dc9fe1ba3167794b5fb1c26cff0c201..754ac5d0480b1f3e249204edfc42244638e8ec44 100644 (file)
@@ -1934,10 +1934,14 @@ DebugCommandProcessor.prototype.profileRequest_ = function(request, response) {
   if (isNaN(modules)) {
     return response.failed('Modules is not an integer');
   }
+  var tag = parseInt(request.arguments.tag);
+  if (isNaN(tag)) {
+    tag = 0;
+  }
   if (request.arguments.command == 'resume') {
-    %ProfilerResume(modules);
+    %ProfilerResume(modules, tag);
   } else if (request.arguments.command == 'pause') {
-    %ProfilerPause(modules);
+    %ProfilerPause(modules, tag);
   } else {
     return response.failed('Unknown command');
   }
index 0e3f998bdf8b81113c9c60062cca62bc131fd903..cee288531b84c0405f0d8a3242114613c3b19846 100644 (file)
@@ -330,6 +330,8 @@ SlidingStateWindow* Logger::sliding_state_window_ = NULL;
 const char** Logger::log_events_ = NULL;
 CompressionHelper* Logger::compression_helper_ = NULL;
 bool Logger::is_logging_ = false;
+int Logger::cpu_profiler_nesting_ = 0;
+int Logger::heap_profiler_nesting_ = 0;
 
 #define DECLARE_LONG_EVENT(ignore1, long_name, ignore2) long_name,
 const char* kLongLogEventsNames[Logger::NUMBER_OF_LOG_EVENTS] = {
@@ -1164,53 +1166,61 @@ int Logger::GetActiveProfilerModules() {
 }
 
 
-void Logger::PauseProfiler(int flags) {
+void Logger::PauseProfiler(int flags, int tag) {
   if (!Log::IsEnabled()) return;
-  const int active_modules = GetActiveProfilerModules();
-  const int modules_to_disable = active_modules & flags;
-  if (modules_to_disable == PROFILER_MODULE_NONE) return;
-
-  if (modules_to_disable & PROFILER_MODULE_CPU) {
-    profiler_->pause();
-    if (FLAG_prof_lazy) {
-      if (!FLAG_sliding_state_window) ticker_->Stop();
-      FLAG_log_code = false;
-      // Must be the same message as Log::kDynamicBufferSeal.
-      LOG(UncheckedStringEvent("profiler", "pause"));
+  if (flags & PROFILER_MODULE_CPU) {
+    // It is OK to have negative nesting.
+    if (--cpu_profiler_nesting_ == 0) {
+      profiler_->pause();
+      if (FLAG_prof_lazy) {
+        if (!FLAG_sliding_state_window) ticker_->Stop();
+        FLAG_log_code = false;
+        // Must be the same message as Log::kDynamicBufferSeal.
+        LOG(UncheckedStringEvent("profiler", "pause"));
+      }
     }
   }
-  if (modules_to_disable &
+  if (flags &
       (PROFILER_MODULE_HEAP_STATS | PROFILER_MODULE_JS_CONSTRUCTORS)) {
-    FLAG_log_gc = false;
+    if (--heap_profiler_nesting_ == 0) {
+      FLAG_log_gc = false;
+    }
   }
-  // Turn off logging if no active modules remain.
-  if ((active_modules & ~flags) == PROFILER_MODULE_NONE) {
+  if (tag != 0) {
+    IntEvent("close-tag", tag);
+  }
+  if (GetActiveProfilerModules() == PROFILER_MODULE_NONE) {
     is_logging_ = false;
   }
 }
 
 
-void Logger::ResumeProfiler(int flags) {
+void Logger::ResumeProfiler(int flags, int tag) {
   if (!Log::IsEnabled()) return;
-  const int modules_to_enable = ~GetActiveProfilerModules() & flags;
-  if (modules_to_enable != PROFILER_MODULE_NONE) {
-    is_logging_ = true;
+  if (tag != 0) {
+    IntEvent("open-tag", tag);
   }
-  if (modules_to_enable & PROFILER_MODULE_CPU) {
-    if (FLAG_prof_lazy) {
-      profiler_->Engage();
-      LOG(UncheckedStringEvent("profiler", "resume"));
-      FLAG_log_code = true;
-      LogCompiledFunctions();
-      LogFunctionObjects();
-      LogAccessorCallbacks();
-      if (!FLAG_sliding_state_window) ticker_->Start();
+  if (flags & PROFILER_MODULE_CPU) {
+    if (cpu_profiler_nesting_++ == 0) {
+      is_logging_ = true;
+      if (FLAG_prof_lazy) {
+        profiler_->Engage();
+        LOG(UncheckedStringEvent("profiler", "resume"));
+        FLAG_log_code = true;
+        LogCompiledFunctions();
+        LogFunctionObjects();
+        LogAccessorCallbacks();
+        if (!FLAG_sliding_state_window) ticker_->Start();
+      }
+      profiler_->resume();
     }
-    profiler_->resume();
   }
-  if (modules_to_enable &
+  if (flags &
       (PROFILER_MODULE_HEAP_STATS | PROFILER_MODULE_JS_CONSTRUCTORS)) {
-    FLAG_log_gc = true;
+    if (heap_profiler_nesting_++ == 0) {
+      is_logging_ = true;
+      FLAG_log_gc = true;
+    }
   }
 }
 
@@ -1219,7 +1229,7 @@ void Logger::ResumeProfiler(int flags) {
 // either from main or Profiler's thread.
 void Logger::StopLoggingAndProfiling() {
   Log::stop();
-  PauseProfiler(PROFILER_MODULE_CPU);
+  PauseProfiler(PROFILER_MODULE_CPU, 0);
 }
 
 
index e68d45805f17882119971a879c1202e0d1fb6a6c..9c2a1502a4072b24ba1c11a670f21f7883b6434c 100644 (file)
--- a/src/log.h
+++ b/src/log.h
@@ -277,8 +277,8 @@ class Logger {
   // Pause/Resume collection of profiling data.
   // When data collection is paused, CPU Tick events are discarded until
   // data collection is Resumed.
-  static void PauseProfiler(int flags);
-  static void ResumeProfiler(int flags);
+  static void PauseProfiler(int flags, int tag);
+  static void ResumeProfiler(int flags, int tag);
   static int GetActiveProfilerModules();
 
   // If logging is performed into a memory buffer, allows to
@@ -379,6 +379,8 @@ class Logger {
   friend class LoggerTestHelper;
 
   static bool is_logging_;
+  static int cpu_profiler_nesting_;
+  static int heap_profiler_nesting_;
 #else
   static bool is_logging() { return false; }
 #endif
index 9cf3cf1a6899ca395bec9e2c01e3fd3583207d22..7767f23d86d6190885d47d56c9d182f23ea43053 100644 (file)
@@ -7917,20 +7917,22 @@ static Object* Runtime_FunctionGetInferredName(Arguments args) {
 
 static Object* Runtime_ProfilerResume(Arguments args) {
   NoHandleAllocation ha;
-  ASSERT(args.length() == 1);
+  ASSERT(args.length() == 2);
 
   CONVERT_CHECKED(Smi, smi_modules, args[0]);
-  v8::V8::ResumeProfilerEx(smi_modules->value());
+  CONVERT_CHECKED(Smi, smi_tag, args[1]);
+  v8::V8::ResumeProfilerEx(smi_modules->value(), smi_tag->value());
   return Heap::undefined_value();
 }
 
 
 static Object* Runtime_ProfilerPause(Arguments args) {
   NoHandleAllocation ha;
-  ASSERT(args.length() == 1);
+  ASSERT(args.length() == 2);
 
   CONVERT_CHECKED(Smi, smi_modules, args[0]);
-  v8::V8::PauseProfilerEx(smi_modules->value());
+  CONVERT_CHECKED(Smi, smi_tag, args[1]);
+  v8::V8::PauseProfilerEx(smi_modules->value(), smi_tag->value());
   return Heap::undefined_value();
 }
 
index 103edd580abdaef083ed12065a23e496756a28d1..e2e5c22124d59949100d609b81442d55c4b9919d 100644 (file)
@@ -328,8 +328,8 @@ namespace internal {
 
 #ifdef ENABLE_LOGGING_AND_PROFILING
 #define RUNTIME_FUNCTION_LIST_PROFILER_SUPPORT(F) \
-  F(ProfilerResume, 1, 1) \
-  F(ProfilerPause, 1, 1)
+  F(ProfilerResume, 2, 1) \
+  F(ProfilerPause, 2, 1)
 #else
 #define RUNTIME_FUNCTION_LIST_PROFILER_SUPPORT(F)
 #endif
index eca2c2b67982799eb063e1d644217d1e4d0b604b..9853af32444fa4c40365b4c7110ced67dc3bcd46 100644 (file)
@@ -170,21 +170,110 @@ static void SigProfSignalHandler(int signal, siginfo_t* info, void* context) {
 #endif  // __linux__
 
 
-static int CheckThatProfilerWorks(int log_pos) {
-  Logger::ResumeProfiler(v8::PROFILER_MODULE_CPU);
+namespace {
+
+class ScopedLoggerInitializer {
+ public:
+  explicit ScopedLoggerInitializer(bool log, bool prof_lazy)
+      : saved_log_(i::FLAG_log),
+        saved_prof_lazy_(i::FLAG_prof_lazy),
+        saved_prof_(i::FLAG_prof),
+        saved_prof_auto_(i::FLAG_prof_auto),
+        trick_to_run_init_flags_(init_flags_(log, prof_lazy)),
+        need_to_set_up_logger_(i::V8::IsRunning()),
+        scope_(),
+        env_(v8::Context::New()) {
+    if (need_to_set_up_logger_) Logger::Setup();
+    env_->Enter();
+  }
+
+  ~ScopedLoggerInitializer() {
+    env_->Exit();
+    Logger::TearDown();
+    i::FLAG_prof_lazy = saved_prof_lazy_;
+    i::FLAG_prof = saved_prof_;
+    i::FLAG_prof_auto = saved_prof_auto_;
+    i::FLAG_log = saved_log_;
+  }
+
+  v8::Handle<v8::Context>& env() { return env_; }
+
+ private:
+  static bool init_flags_(bool log, bool prof_lazy) {
+    i::FLAG_log = log;
+    i::FLAG_prof = true;
+    i::FLAG_prof_lazy = prof_lazy;
+    i::FLAG_prof_auto = false;
+    i::FLAG_logfile = "*";
+    return prof_lazy;
+  }
+
+  const bool saved_log_;
+  const bool saved_prof_lazy_;
+  const bool saved_prof_;
+  const bool saved_prof_auto_;
+  const bool trick_to_run_init_flags_;
+  const bool need_to_set_up_logger_;
+  v8::HandleScope scope_;
+  v8::Handle<v8::Context> env_;
+
+  DISALLOW_COPY_AND_ASSIGN(ScopedLoggerInitializer);
+};
+
+
+class LogBufferMatcher {
+ public:
+  LogBufferMatcher() {
+    // Skip all initially logged stuff.
+    log_pos_ = GetLogLines(0, &buffer_);
+  }
+
+  int log_pos() { return log_pos_; }
+
+  int GetNextChunk() {
+    int chunk_size = GetLogLines(log_pos_, &buffer_);
+    CHECK_GT(buffer_.length(), chunk_size);
+    buffer_[chunk_size] = '\0';
+    log_pos_ += chunk_size;
+    return chunk_size;
+  }
+
+  const char* Find(const char* substr) {
+    return strstr(buffer_.start(), substr);
+  }
+
+  const char* Find(const i::Vector<char>& substr) {
+    return Find(substr.start());
+  }
+
+  bool IsInSequence(const char* s1, const char* s2) {
+    const char* s1_pos = Find(s1);
+    const char* s2_pos = Find(s2);
+    CHECK_NE(NULL, s1_pos);
+    CHECK_NE(NULL, s2_pos);
+    return s1_pos < s2_pos;
+  }
+
+  void PrintBuffer() {
+    puts(buffer_.start());
+  }
+
+ private:
+  EmbeddedVector<char, 102400> buffer_;
+  int log_pos_;
+};
+
+}  // namespace
+
+
+static void CheckThatProfilerWorks(LogBufferMatcher* matcher) {
+  Logger::ResumeProfiler(v8::PROFILER_MODULE_CPU, 0);
   CHECK(LoggerTestHelper::IsSamplerActive());
 
   // Verify that the current map of compiled functions has been logged.
-  EmbeddedVector<char, 102400> buffer;
-  int map_log_size = GetLogLines(log_pos, &buffer);
-  printf("map_log_size: %d\n", map_log_size);
-  CHECK_GT(map_log_size, 0);
-  CHECK_GT(buffer.length(), map_log_size);
-  log_pos += map_log_size;
-  // Check buffer contents.
-  buffer[map_log_size] = '\0';
+  CHECK_GT(matcher->GetNextChunk(), 0);
   const char* code_creation = "\ncode-creation,";  // eq. to /^code-creation,/
-  CHECK_NE(NULL, strstr(buffer.start(), code_creation));
+  CHECK_NE(NULL, matcher->Find(code_creation));
 
 #ifdef __linux__
   // Intercept SIGPROF handler to make sure that the test process
@@ -204,7 +293,7 @@ static int CheckThatProfilerWorks(int log_pos) {
   i::OS::SNPrintF(script_src,
                   "function f%d(x) { return %d * x; }"
                   "for (var i = 0; i < 10000; ++i) { f%d(i); }",
-                  log_pos, log_pos, log_pos);
+                  matcher->log_pos(), matcher->log_pos(), matcher->log_pos());
   // Run code for 200 msecs to get some ticks.
   const double end_time = i::OS::TimeCurrentMillis() + 200;
   while (i::OS::TimeCurrentMillis() < end_time) {
@@ -213,7 +302,7 @@ static int CheckThatProfilerWorks(int log_pos) {
     i::OS::Sleep(1);
   }
 
-  Logger::PauseProfiler(v8::PROFILER_MODULE_CPU);
+  Logger::PauseProfiler(v8::PROFILER_MODULE_CPU, 0);
   CHECK(!LoggerTestHelper::IsSamplerActive());
 
   // Wait 50 msecs to allow Profiler thread to process the last
@@ -221,68 +310,39 @@ static int CheckThatProfilerWorks(int log_pos) {
   i::OS::Sleep(50);
 
   // Now we must have compiler and tick records.
-  int log_size = GetLogLines(log_pos, &buffer);
-  printf("log_size: %d\n", log_size);
-  CHECK_GT(log_size, 0);
-  CHECK_GT(buffer.length(), log_size);
-  log_pos += log_size;
-  // Check buffer contents.
-  buffer[log_size] = '\0';
-  printf("%s", buffer.start());
+  CHECK_GT(matcher->GetNextChunk(), 0);
+  matcher->PrintBuffer();
+  CHECK_NE(NULL, matcher->Find(code_creation));
   const char* tick = "\ntick,";
-  CHECK_NE(NULL, strstr(buffer.start(), code_creation));
-  const bool ticks_found = strstr(buffer.start(), tick) != NULL;
+  const bool ticks_found = matcher->Find(tick) != NULL;
   CHECK_EQ(was_sigprof_received, ticks_found);
-
-  return log_pos;
 }
 
 
 TEST(ProfLazyMode) {
-  const bool saved_prof_lazy = i::FLAG_prof_lazy;
-  const bool saved_prof = i::FLAG_prof;
-  const bool saved_prof_auto = i::FLAG_prof_auto;
-  i::FLAG_prof = true;
-  i::FLAG_prof_lazy = true;
-  i::FLAG_prof_auto = false;
-  i::FLAG_logfile = "*";
-
-  // If tests are being run manually, V8 will be already initialized
-  // by the bottom test.
-  const bool need_to_set_up_logger = i::V8::IsRunning();
-  v8::HandleScope scope;
-  v8::Handle<v8::Context> env = v8::Context::New();
-  if (need_to_set_up_logger) Logger::Setup();
-  env->Enter();
+  ScopedLoggerInitializer initialize_logger(false, true);
 
   // No sampling should happen prior to resuming profiler.
   CHECK(!LoggerTestHelper::IsSamplerActive());
 
-  EmbeddedVector<char, 102400> buffer;
+  LogBufferMatcher matcher;
   // Nothing must be logged until profiling is resumed.
-  int log_pos = GetLogLines(0, &buffer);
-  CHECK_EQ(0, log_pos);
+  CHECK_EQ(0, matcher.log_pos());
 
   CompileAndRunScript("var a = (function(x) { return x + 1; })(10);");
 
   // Nothing must be logged while profiling is suspended.
-  CHECK_EQ(0, GetLogLines(log_pos, &buffer));
+  CHECK_EQ(0, matcher.GetNextChunk());
 
-  log_pos = CheckThatProfilerWorks(log_pos);
+  CheckThatProfilerWorks(&matcher);
 
   CompileAndRunScript("var a = (function(x) { return x + 1; })(10);");
 
   // No new data beyond last retrieved position.
-  CHECK_EQ(0, GetLogLines(log_pos, &buffer));
+  CHECK_EQ(0, matcher.GetNextChunk());
 
   // Check that profiling can be resumed again.
-  CheckThatProfilerWorks(log_pos);
-
-  env->Exit();
-  Logger::TearDown();
-  i::FLAG_prof_lazy = saved_prof_lazy;
-  i::FLAG_prof = saved_prof;
-  i::FLAG_prof_auto = saved_prof_auto;
+  CheckThatProfilerWorks(&matcher);
 }
 
 
@@ -480,25 +540,8 @@ static v8::Handle<v8::Value> ObjMethod1(const v8::Arguments& args) {
 }
 
 TEST(LogCallbacks) {
-  const bool saved_prof_lazy = i::FLAG_prof_lazy;
-  const bool saved_prof = i::FLAG_prof;
-  const bool saved_prof_auto = i::FLAG_prof_auto;
-  i::FLAG_prof = true;
-  i::FLAG_prof_lazy = false;
-  i::FLAG_prof_auto = false;
-  i::FLAG_logfile = "*";
-
-  // If tests are being run manually, V8 will be already initialized
-  // by the bottom test.
-  const bool need_to_set_up_logger = i::V8::IsRunning();
-  v8::HandleScope scope;
-  v8::Handle<v8::Context> env = v8::Context::New();
-  if (need_to_set_up_logger) Logger::Setup();
-  env->Enter();
-
-  // Skip all initially logged stuff.
-  EmbeddedVector<char, 102400> buffer;
-  int log_pos = GetLogLines(0, &buffer);
+  ScopedLoggerInitializer initialize_logger(false, false);
+  LogBufferMatcher matcher;
 
   v8::Persistent<v8::FunctionTemplate> obj =
       v8::Persistent<v8::FunctionTemplate>::New(v8::FunctionTemplate::New());
@@ -511,16 +554,14 @@ TEST(LogCallbacks) {
                                        signature),
              static_cast<v8::PropertyAttribute>(v8::DontDelete));
 
-  env->Global()->Set(v8_str("Obj"), obj->GetFunction());
+  initialize_logger.env()->Global()->Set(v8_str("Obj"), obj->GetFunction());
   CompileAndRunScript("Obj.prototype.method1.toString();");
 
   i::Logger::LogCompiledFunctions();
-  log_pos = GetLogLines(log_pos, &buffer);
-  CHECK_GT(log_pos, 0);
-  buffer[log_pos] = 0;
+  CHECK_GT(matcher.GetNextChunk(), 0);
 
   const char* callback_rec = "code-creation,Callback,";
-  char* pos = strstr(buffer.start(), callback_rec);
+  char* pos = const_cast<char*>(matcher.Find(callback_rec));
   CHECK_NE(NULL, pos);
   pos += strlen(callback_rec);
   EmbeddedVector<char, 100> ref_data;
@@ -530,12 +571,6 @@ TEST(LogCallbacks) {
   CHECK_EQ(ref_data.start(), pos);
 
   obj.Dispose();
-
-  env->Exit();
-  Logger::TearDown();
-  i::FLAG_prof_lazy = saved_prof_lazy;
-  i::FLAG_prof = saved_prof;
-  i::FLAG_prof_auto = saved_prof_auto;
 }
 
 
@@ -555,25 +590,8 @@ static v8::Handle<v8::Value> Prop2Getter(v8::Local<v8::String> property,
 }
 
 TEST(LogAccessorCallbacks) {
-  const bool saved_prof_lazy = i::FLAG_prof_lazy;
-  const bool saved_prof = i::FLAG_prof;
-  const bool saved_prof_auto = i::FLAG_prof_auto;
-  i::FLAG_prof = true;
-  i::FLAG_prof_lazy = false;
-  i::FLAG_prof_auto = false;
-  i::FLAG_logfile = "*";
-
-  // If tests are being run manually, V8 will be already initialized
-  // by the bottom test.
-  const bool need_to_set_up_logger = i::V8::IsRunning();
-  v8::HandleScope scope;
-  v8::Handle<v8::Context> env = v8::Context::New();
-  if (need_to_set_up_logger) Logger::Setup();
-  env->Enter();
-
-  // Skip all initially logged stuff.
-  EmbeddedVector<char, 102400> buffer;
-  int log_pos = GetLogLines(0, &buffer);
+  ScopedLoggerInitializer initialize_logger(false, false);
+  LogBufferMatcher matcher;
 
   v8::Persistent<v8::FunctionTemplate> obj =
       v8::Persistent<v8::FunctionTemplate>::New(v8::FunctionTemplate::New());
@@ -583,34 +601,112 @@ TEST(LogAccessorCallbacks) {
   inst->SetAccessor(v8::String::New("prop2"), Prop2Getter);
 
   i::Logger::LogAccessorCallbacks();
-  log_pos = GetLogLines(log_pos, &buffer);
-  CHECK_GT(log_pos, 0);
-  buffer[log_pos] = 0;
-  printf("%s", buffer.start());
+  CHECK_GT(matcher.GetNextChunk(), 0);
+  matcher.PrintBuffer();
 
   EmbeddedVector<char, 100> prop1_getter_record;
   i::OS::SNPrintF(prop1_getter_record,
                   "code-creation,Callback,0x%" V8PRIxPTR ",1,\"get prop1\"",
                   Prop1Getter);
-  CHECK_NE(NULL, strstr(buffer.start(), prop1_getter_record.start()));
+  CHECK_NE(NULL, matcher.Find(prop1_getter_record));
   EmbeddedVector<char, 100> prop1_setter_record;
   i::OS::SNPrintF(prop1_setter_record,
                   "code-creation,Callback,0x%" V8PRIxPTR ",1,\"set prop1\"",
                   Prop1Setter);
-  CHECK_NE(NULL, strstr(buffer.start(), prop1_setter_record.start()));
+  CHECK_NE(NULL, matcher.Find(prop1_setter_record));
   EmbeddedVector<char, 100> prop2_getter_record;
   i::OS::SNPrintF(prop2_getter_record,
                   "code-creation,Callback,0x%" V8PRIxPTR ",1,\"get prop2\"",
                   Prop2Getter);
-  CHECK_NE(NULL, strstr(buffer.start(), prop2_getter_record.start()));
+  CHECK_NE(NULL, matcher.Find(prop2_getter_record));
 
   obj.Dispose();
+}
 
-  env->Exit();
-  Logger::TearDown();
-  i::FLAG_prof_lazy = saved_prof_lazy;
-  i::FLAG_prof = saved_prof;
-  i::FLAG_prof_auto = saved_prof_auto;
+
+TEST(LogTags) {
+  ScopedLoggerInitializer initialize_logger(true, false);
+  LogBufferMatcher matcher;
+
+  const char* open_tag = "open-tag,";
+  const char* close_tag = "close-tag,";
+
+  // Check compatibility with the old style behavior.
+  CHECK_EQ(v8::PROFILER_MODULE_NONE, Logger::GetActiveProfilerModules());
+  Logger::ResumeProfiler(v8::PROFILER_MODULE_CPU, 0);
+  CHECK_EQ(v8::PROFILER_MODULE_CPU, Logger::GetActiveProfilerModules());
+  Logger::PauseProfiler(v8::PROFILER_MODULE_CPU, 0);
+  CHECK_EQ(v8::PROFILER_MODULE_NONE, Logger::GetActiveProfilerModules());
+  CHECK_EQ(NULL, matcher.Find(open_tag));
+  CHECK_EQ(NULL, matcher.Find(close_tag));
+
+  const char* open_tag1 = "open-tag,1\n";
+  const char* close_tag1 = "close-tag,1\n";
+
+  // Check non-nested tag case.
+  CHECK_EQ(v8::PROFILER_MODULE_NONE, Logger::GetActiveProfilerModules());
+  Logger::ResumeProfiler(v8::PROFILER_MODULE_CPU, 1);
+  CHECK_EQ(v8::PROFILER_MODULE_CPU, Logger::GetActiveProfilerModules());
+  Logger::PauseProfiler(v8::PROFILER_MODULE_CPU, 1);
+  CHECK_EQ(v8::PROFILER_MODULE_NONE, Logger::GetActiveProfilerModules());
+  CHECK_GT(matcher.GetNextChunk(), 0);
+  CHECK(matcher.IsInSequence(open_tag1, close_tag1));
+
+  const char* open_tag2 = "open-tag,2\n";
+  const char* close_tag2 = "close-tag,2\n";
+
+  // Check nested tags case.
+  CHECK_EQ(v8::PROFILER_MODULE_NONE, Logger::GetActiveProfilerModules());
+  Logger::ResumeProfiler(v8::PROFILER_MODULE_CPU, 1);
+  CHECK_EQ(v8::PROFILER_MODULE_CPU, Logger::GetActiveProfilerModules());
+  Logger::ResumeProfiler(v8::PROFILER_MODULE_CPU, 2);
+  CHECK_EQ(v8::PROFILER_MODULE_CPU, Logger::GetActiveProfilerModules());
+  Logger::PauseProfiler(v8::PROFILER_MODULE_CPU, 2);
+  CHECK_EQ(v8::PROFILER_MODULE_CPU, Logger::GetActiveProfilerModules());
+  Logger::PauseProfiler(v8::PROFILER_MODULE_CPU, 1);
+  CHECK_EQ(v8::PROFILER_MODULE_NONE, Logger::GetActiveProfilerModules());
+  CHECK_GT(matcher.GetNextChunk(), 0);
+  // open_tag1 < open_tag2 < close_tag2 < close_tag1
+  CHECK(matcher.IsInSequence(open_tag1, open_tag2));
+  CHECK(matcher.IsInSequence(open_tag2, close_tag2));
+  CHECK(matcher.IsInSequence(close_tag2, close_tag1));
+
+  // Check overlapped tags case.
+  CHECK_EQ(v8::PROFILER_MODULE_NONE, Logger::GetActiveProfilerModules());
+  Logger::ResumeProfiler(v8::PROFILER_MODULE_CPU, 1);
+  CHECK_EQ(v8::PROFILER_MODULE_CPU, Logger::GetActiveProfilerModules());
+  Logger::ResumeProfiler(v8::PROFILER_MODULE_CPU, 2);
+  CHECK_EQ(v8::PROFILER_MODULE_CPU, Logger::GetActiveProfilerModules());
+  Logger::PauseProfiler(v8::PROFILER_MODULE_CPU, 1);
+  CHECK_EQ(v8::PROFILER_MODULE_CPU, Logger::GetActiveProfilerModules());
+  Logger::PauseProfiler(v8::PROFILER_MODULE_CPU, 2);
+  CHECK_EQ(v8::PROFILER_MODULE_NONE, Logger::GetActiveProfilerModules());
+  CHECK_GT(matcher.GetNextChunk(), 0);
+  // open_tag1 < open_tag2 < close_tag1 < close_tag2
+  CHECK(matcher.IsInSequence(open_tag1, open_tag2));
+  CHECK(matcher.IsInSequence(open_tag2, close_tag1));
+  CHECK(matcher.IsInSequence(close_tag1, close_tag2));
+
+  const char* open_tag3 = "open-tag,3\n";
+  const char* close_tag3 = "close-tag,3\n";
+
+  // Check pausing overflow case.
+  CHECK_EQ(v8::PROFILER_MODULE_NONE, Logger::GetActiveProfilerModules());
+  Logger::ResumeProfiler(v8::PROFILER_MODULE_CPU, 1);
+  CHECK_EQ(v8::PROFILER_MODULE_CPU, Logger::GetActiveProfilerModules());
+  Logger::ResumeProfiler(v8::PROFILER_MODULE_CPU, 2);
+  CHECK_EQ(v8::PROFILER_MODULE_CPU, Logger::GetActiveProfilerModules());
+  Logger::PauseProfiler(v8::PROFILER_MODULE_CPU, 2);
+  CHECK_EQ(v8::PROFILER_MODULE_CPU, Logger::GetActiveProfilerModules());
+  Logger::PauseProfiler(v8::PROFILER_MODULE_CPU, 1);
+  CHECK_EQ(v8::PROFILER_MODULE_NONE, Logger::GetActiveProfilerModules());
+  Logger::PauseProfiler(v8::PROFILER_MODULE_CPU, 3);
+  CHECK_EQ(v8::PROFILER_MODULE_NONE, Logger::GetActiveProfilerModules());
+  Logger::ResumeProfiler(v8::PROFILER_MODULE_CPU, 3);
+  CHECK_EQ(v8::PROFILER_MODULE_NONE, Logger::GetActiveProfilerModules());
+  // Must be no tags, because logging must be disabled.
+  CHECK_EQ(NULL, matcher.Find(open_tag3));
+  CHECK_EQ(NULL, matcher.Find(close_tag3));
 }