Move V8 to external/v8

Change-Id: If68025d67453785a651c5dfb34fad298c16676a4
diff --git a/test/cctest/test-log.cc b/test/cctest/test-log.cc
new file mode 100644
index 0000000..65ab50a
--- /dev/null
+++ b/test/cctest/test-log.cc
@@ -0,0 +1,898 @@
+// Copyright 2006-2009 the V8 project authors. All rights reserved.
+//
+// Tests of logging functions from log.h
+
+#ifdef ENABLE_LOGGING_AND_PROFILING
+
+#ifdef __linux__
+#include <math.h>
+#include <pthread.h>
+#include <signal.h>
+#include <unistd.h>
+#endif  // __linux__
+
+#include "v8.h"
+#include "log.h"
+#include "v8threads.h"
+#include "cctest.h"
+
+using v8::internal::Address;
+using v8::internal::EmbeddedVector;
+using v8::internal::Logger;
+
+namespace i = v8::internal;
+
+static void SetUp() {
+  // Log to memory buffer.
+  i::FLAG_logfile = "*";
+  i::FLAG_log = true;
+  Logger::Setup();
+}
+
+static void TearDown() {
+  Logger::TearDown();
+}
+
+
+TEST(EmptyLog) {
+  SetUp();
+  CHECK_EQ(0, Logger::GetLogLines(0, NULL, 0));
+  CHECK_EQ(0, Logger::GetLogLines(100, NULL, 0));
+  CHECK_EQ(0, Logger::GetLogLines(0, NULL, 100));
+  CHECK_EQ(0, Logger::GetLogLines(100, NULL, 100));
+  TearDown();
+}
+
+
+TEST(GetMessages) {
+  SetUp();
+  Logger::StringEvent("aaa", "bbb");
+  Logger::StringEvent("cccc", "dddd");
+  CHECK_EQ(0, Logger::GetLogLines(0, NULL, 0));
+  char log_lines[100];
+  memset(log_lines, 0, sizeof(log_lines));
+  // Requesting data size which is smaller than first log message length.
+  CHECK_EQ(0, Logger::GetLogLines(0, log_lines, 3));
+  // See Logger::StringEvent.
+  const char* line_1 = "aaa,\"bbb\"\n";
+  const int line_1_len = strlen(line_1);
+  // Still smaller than log message length.
+  CHECK_EQ(0, Logger::GetLogLines(0, log_lines, line_1_len - 1));
+  // The exact size.
+  CHECK_EQ(line_1_len, Logger::GetLogLines(0, log_lines, line_1_len));
+  CHECK_EQ(line_1, log_lines);
+  memset(log_lines, 0, sizeof(log_lines));
+  // A bit more than the first line length.
+  CHECK_EQ(line_1_len, Logger::GetLogLines(0, log_lines, line_1_len + 3));
+  log_lines[line_1_len] = '\0';
+  CHECK_EQ(line_1, log_lines);
+  memset(log_lines, 0, sizeof(log_lines));
+  const char* line_2 = "cccc,\"dddd\"\n";
+  const int line_2_len = strlen(line_2);
+  // Now start with line_2 beginning.
+  CHECK_EQ(0, Logger::GetLogLines(line_1_len, log_lines, 0));
+  CHECK_EQ(0, Logger::GetLogLines(line_1_len, log_lines, 3));
+  CHECK_EQ(0, Logger::GetLogLines(line_1_len, log_lines, line_2_len - 1));
+  CHECK_EQ(line_2_len, Logger::GetLogLines(line_1_len, log_lines, line_2_len));
+  CHECK_EQ(line_2, log_lines);
+  memset(log_lines, 0, sizeof(log_lines));
+  CHECK_EQ(line_2_len,
+           Logger::GetLogLines(line_1_len, log_lines, line_2_len + 3));
+  CHECK_EQ(line_2, log_lines);
+  memset(log_lines, 0, sizeof(log_lines));
+  // Now get entire buffer contents.
+  const char* all_lines = "aaa,\"bbb\"\ncccc,\"dddd\"\n";
+  const int all_lines_len = strlen(all_lines);
+  CHECK_EQ(all_lines_len, Logger::GetLogLines(0, log_lines, all_lines_len));
+  CHECK_EQ(all_lines, log_lines);
+  memset(log_lines, 0, sizeof(log_lines));
+  CHECK_EQ(all_lines_len, Logger::GetLogLines(0, log_lines, all_lines_len + 3));
+  CHECK_EQ(all_lines, log_lines);
+  memset(log_lines, 0, sizeof(log_lines));
+  TearDown();
+}
+
+
+static int GetLogLines(int start_pos, i::Vector<char>* buffer) {
+  return Logger::GetLogLines(start_pos, buffer->start(), buffer->length());
+}
+
+
+TEST(BeyondWritePosition) {
+  SetUp();
+  Logger::StringEvent("aaa", "bbb");
+  Logger::StringEvent("cccc", "dddd");
+  // See Logger::StringEvent.
+  const char* all_lines = "aaa,\"bbb\"\ncccc,\"dddd\"\n";
+  const int all_lines_len = strlen(all_lines);
+  EmbeddedVector<char, 100> buffer;
+  const int beyond_write_pos = all_lines_len;
+  CHECK_EQ(0, Logger::GetLogLines(beyond_write_pos, buffer.start(), 1));
+  CHECK_EQ(0, GetLogLines(beyond_write_pos, &buffer));
+  CHECK_EQ(0, Logger::GetLogLines(beyond_write_pos + 1, buffer.start(), 1));
+  CHECK_EQ(0, GetLogLines(beyond_write_pos + 1, &buffer));
+  CHECK_EQ(0, Logger::GetLogLines(beyond_write_pos + 100, buffer.start(), 1));
+  CHECK_EQ(0, GetLogLines(beyond_write_pos + 100, &buffer));
+  CHECK_EQ(0, Logger::GetLogLines(10 * 1024 * 1024, buffer.start(), 1));
+  CHECK_EQ(0, GetLogLines(10 * 1024 * 1024, &buffer));
+  TearDown();
+}
+
+
+TEST(MemoryLoggingTurnedOff) {
+  // Log to stdout
+  i::FLAG_logfile = "-";
+  i::FLAG_log = true;
+  Logger::Setup();
+  CHECK_EQ(0, Logger::GetLogLines(0, NULL, 0));
+  CHECK_EQ(0, Logger::GetLogLines(100, NULL, 0));
+  CHECK_EQ(0, Logger::GetLogLines(0, NULL, 100));
+  CHECK_EQ(0, Logger::GetLogLines(100, NULL, 100));
+  Logger::TearDown();
+}
+
+
+static void CompileAndRunScript(const char *src) {
+  v8::Script::Compile(v8::String::New(src))->Run();
+}
+
+
+namespace v8 {
+namespace internal {
+
+class LoggerTestHelper : public AllStatic {
+ public:
+  static bool IsSamplerActive() { return Logger::IsProfilerSamplerActive(); }
+};
+
+}  // namespace v8::internal
+}  // namespace v8
+
+using v8::internal::LoggerTestHelper;
+
+
+// Under Linux, we need to check if signals were delivered to avoid false
+// positives.  Under other platforms profiling is done via a high-priority
+// thread, so this case never happen.
+static bool was_sigprof_received = true;
+#ifdef __linux__
+
+struct sigaction old_sigprof_handler;
+pthread_t our_thread;
+
+static void SigProfSignalHandler(int signal, siginfo_t* info, void* context) {
+  if (signal != SIGPROF || !pthread_equal(pthread_self(), our_thread)) return;
+  was_sigprof_received = true;
+  old_sigprof_handler.sa_sigaction(signal, info, context);
+}
+
+#endif  // __linux__
+
+
+static int CheckThatProfilerWorks(int log_pos) {
+  Logger::ResumeProfiler(v8::PROFILER_MODULE_CPU);
+  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';
+  const char* code_creation = "\ncode-creation,";  // eq. to /^code-creation,/
+  CHECK_NE(NULL, strstr(buffer.start(), code_creation));
+
+#ifdef __linux__
+  // Intercept SIGPROF handler to make sure that the test process
+  // had received it. Under load, system can defer it causing test failure.
+  // It is important to execute this after 'ResumeProfiler'.
+  our_thread = pthread_self();
+  was_sigprof_received = false;
+  struct sigaction sa;
+  sa.sa_sigaction = SigProfSignalHandler;
+  sigemptyset(&sa.sa_mask);
+  sa.sa_flags = SA_SIGINFO;
+  CHECK_EQ(0, sigaction(SIGPROF, &sa, &old_sigprof_handler));
+#endif  // __linux__
+
+  // Force compiler to generate new code by parametrizing source.
+  EmbeddedVector<char, 100> script_src;
+  i::OS::SNPrintF(script_src,
+                  "for (var i = 0; i < 1000; ++i) { "
+                  "(function(x) { return %d * x; })(i); }",
+                  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) {
+    CompileAndRunScript(script_src.start());
+    // Yield CPU to give Profiler thread a chance to process ticks.
+    i::OS::Sleep(1);
+  }
+
+  Logger::PauseProfiler(v8::PROFILER_MODULE_CPU);
+  CHECK(!LoggerTestHelper::IsSamplerActive());
+
+  // Wait 50 msecs to allow Profiler thread to process the last
+  // tick sample it has got.
+  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';
+  const char* tick = "\ntick,";
+  CHECK_NE(NULL, strstr(buffer.start(), code_creation));
+  const bool ticks_found = strstr(buffer.start(), 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 test below.
+  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();
+
+  // No sampling should happen prior to resuming profiler.
+  CHECK(!LoggerTestHelper::IsSamplerActive());
+
+  // Read initial logged data (static libs map).
+  EmbeddedVector<char, 102400> buffer;
+  int log_pos = GetLogLines(0, &buffer);
+  CHECK_GT(log_pos, 0);
+  CHECK_GT(buffer.length(), 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));
+
+  log_pos = CheckThatProfilerWorks(log_pos);
+
+  CompileAndRunScript("var a = (function(x) { return x + 1; })(10);");
+
+  // No new data beyond last retrieved position.
+  CHECK_EQ(0, GetLogLines(log_pos, &buffer));
+
+  // 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;
+}
+
+
+// Profiling multiple threads that use V8 is currently only available on Linux.
+#ifdef __linux__
+
+namespace {
+
+class LoopingThread : public v8::internal::Thread {
+ public:
+  LoopingThread()
+      : v8::internal::Thread(),
+        semaphore_(v8::internal::OS::CreateSemaphore(0)),
+        run_(true) {
+  }
+
+  virtual ~LoopingThread() { delete semaphore_; }
+
+  void Run() {
+    self_ = pthread_self();
+    RunLoop();
+  }
+
+  void SendSigProf() { pthread_kill(self_, SIGPROF); }
+
+  void Stop() { run_ = false; }
+
+  bool WaitForRunning() { return semaphore_->Wait(1000000); }
+
+ protected:
+  bool IsRunning() { return run_; }
+
+  virtual void RunLoop() = 0;
+
+  void SetV8ThreadId() {
+    v8_thread_id_ = v8::V8::GetCurrentThreadId();
+  }
+
+  void SignalRunning() { semaphore_->Signal(); }
+
+ private:
+  v8::internal::Semaphore* semaphore_;
+  bool run_;
+  pthread_t self_;
+  int v8_thread_id_;
+};
+
+
+class LoopingJsThread : public LoopingThread {
+ public:
+  void RunLoop() {
+    {
+      v8::Locker locker;
+      CHECK(v8::internal::ThreadManager::HasId());
+      SetV8ThreadId();
+    }
+    while (IsRunning()) {
+      v8::Locker locker;
+      v8::HandleScope scope;
+      v8::Persistent<v8::Context> context = v8::Context::New();
+      v8::Context::Scope context_scope(context);
+      SignalRunning();
+      CompileAndRunScript(
+          "var j; for (var i=0; i<10000; ++i) { j = Math.sin(i); }");
+      context.Dispose();
+      i::OS::Sleep(1);
+    }
+  }
+};
+
+
+class LoopingNonJsThread : public LoopingThread {
+ public:
+  void RunLoop() {
+    v8::Locker locker;
+    v8::Unlocker unlocker;
+    // Now thread has V8's id, but will not run VM code.
+    CHECK(v8::internal::ThreadManager::HasId());
+    double i = 10;
+    SignalRunning();
+    while (IsRunning()) {
+      i = sin(i);
+      i::OS::Sleep(1);
+    }
+  }
+};
+
+
+class TestSampler : public v8::internal::Sampler {
+ public:
+  TestSampler()
+      : Sampler(0, true),
+        semaphore_(v8::internal::OS::CreateSemaphore(0)),
+        was_sample_stack_called_(false) {
+  }
+
+  ~TestSampler() { delete semaphore_; }
+
+  void SampleStack(v8::internal::TickSample*) {
+    was_sample_stack_called_ = true;
+  }
+
+  void Tick(v8::internal::TickSample*) { semaphore_->Signal(); }
+
+  bool WaitForTick() { return semaphore_->Wait(1000000); }
+
+  void Reset() { was_sample_stack_called_ = false; }
+
+  bool WasSampleStackCalled() { return was_sample_stack_called_; }
+
+ private:
+  v8::internal::Semaphore* semaphore_;
+  bool was_sample_stack_called_;
+};
+
+
+}  // namespace
+
+TEST(ProfMultipleThreads) {
+  LoopingJsThread jsThread;
+  jsThread.Start();
+  LoopingNonJsThread nonJsThread;
+  nonJsThread.Start();
+
+  TestSampler sampler;
+  sampler.Start();
+  CHECK(!sampler.WasSampleStackCalled());
+  jsThread.WaitForRunning();
+  jsThread.SendSigProf();
+  CHECK(sampler.WaitForTick());
+  CHECK(sampler.WasSampleStackCalled());
+  sampler.Reset();
+  CHECK(!sampler.WasSampleStackCalled());
+  nonJsThread.WaitForRunning();
+  nonJsThread.SendSigProf();
+  CHECK(sampler.WaitForTick());
+  CHECK(!sampler.WasSampleStackCalled());
+  sampler.Stop();
+
+  jsThread.Stop();
+  nonJsThread.Stop();
+  jsThread.Join();
+  nonJsThread.Join();
+}
+
+#endif  // __linux__
+
+
+static inline bool IsStringEqualTo(const char* r, const char* s) {
+  return strncmp(r, s, strlen(r)) == 0;
+}
+
+
+static bool Consume(const char* str, char** buf) {
+  if (IsStringEqualTo(str, *buf)) {
+    *buf += strlen(str);
+    return true;
+  }
+  return false;
+}
+
+
+namespace {
+
+// A code entity is a pointer to a position of code-creation event in buffer log
+// offset to a point where entity size begins, i.e.: '255,"func"\n'. This makes
+// comparing code entities pretty easy.
+typedef char* CodeEntityInfo;
+
+class Interval {
+ public:
+  Interval()
+      : min_addr_(reinterpret_cast<Address>(-1)),
+        max_addr_(reinterpret_cast<Address>(0)), next_(NULL) {}
+
+  ~Interval() { delete next_; }
+
+  size_t Length() {
+    size_t result = max_addr_ - min_addr_ + 1;
+    if (next_ != NULL) result += next_->Length();
+    return result;
+  }
+
+  void CloneFrom(Interval* src) {
+    while (src != NULL) {
+      RegisterAddress(src->min_addr_);
+      RegisterAddress(src->max_addr_);
+      src = src->next_;
+    }
+  }
+
+  bool Contains(Address addr) {
+    if (min_addr_ <= addr && addr <= max_addr_) {
+      return true;
+    }
+    if (next_ != NULL) {
+      return next_->Contains(addr);
+    } else {
+      return false;
+    }
+  }
+
+  size_t GetIndex(Address addr) {
+    if (min_addr_ <= addr && addr <= max_addr_) {
+      return addr - min_addr_;
+    }
+    CHECK_NE(NULL, next_);
+    return (max_addr_ - min_addr_ + 1) + next_->GetIndex(addr);
+  }
+
+  Address GetMinAddr() {
+    return next_ == NULL ? min_addr_ : i::Min(min_addr_, next_->GetMinAddr());
+  }
+
+  Address GetMaxAddr() {
+    return next_ == NULL ? max_addr_ : i::Max(max_addr_, next_->GetMaxAddr());
+  }
+
+  void RegisterAddress(Address addr) {
+    if (min_addr_ == reinterpret_cast<Address>(-1)
+        || (size_t)(addr > min_addr_ ?
+           addr - min_addr_ : min_addr_ - addr) < MAX_DELTA) {
+      if (addr < min_addr_) min_addr_ = addr;
+      if (addr > max_addr_) max_addr_ = addr;
+    } else {
+      if (next_ == NULL) next_ = new Interval();
+      next_->RegisterAddress(addr);
+    }
+  }
+
+  Address raw_min_addr() { return min_addr_; }
+
+  Address raw_max_addr() { return max_addr_; }
+
+  Interval* get_next() { return next_; }
+
+ private:
+  static const size_t MAX_DELTA = 0x100000;
+  Address min_addr_;
+  Address max_addr_;
+  Interval* next_;
+};
+
+
+// A structure used to return log parsing results.
+class ParseLogResult {
+ public:
+  ParseLogResult()
+      : entities_map(NULL), entities(NULL),
+        max_entities(0) {}
+
+  ~ParseLogResult() {
+    i::DeleteArray(entities_map);
+    i::DeleteArray(entities);
+  }
+
+  void AllocateEntities() {
+    // Make sure that the test doesn't operate on a bogus log.
+    CHECK_GT(max_entities, 0);
+    CHECK_GT(bounds.GetMinAddr(), 0);
+    CHECK_GT(bounds.GetMaxAddr(), bounds.GetMinAddr());
+
+    entities = i::NewArray<CodeEntityInfo>(max_entities);
+    for (int i = 0; i < max_entities; ++i) {
+      entities[i] = NULL;
+    }
+    const size_t map_length = bounds.Length();
+    entities_map = i::NewArray<int>(map_length);
+    for (size_t i = 0; i < map_length; ++i) {
+      entities_map[i] = -1;
+    }
+  }
+
+  bool HasIndexForAddress(Address addr) {
+    return bounds.Contains(addr);
+  }
+
+  size_t GetIndexForAddress(Address addr) {
+    CHECK(HasIndexForAddress(addr));
+    return bounds.GetIndex(addr);
+  }
+
+  CodeEntityInfo GetEntity(Address addr) {
+    if (HasIndexForAddress(addr)) {
+      size_t idx = GetIndexForAddress(addr);
+      int item = entities_map[idx];
+      return item != -1 ? entities[item] : NULL;
+    }
+    return NULL;
+  }
+
+  void ParseAddress(char* start) {
+    Address addr =
+        reinterpret_cast<Address>(strtoul(start, NULL, 16));  // NOLINT
+    bounds.RegisterAddress(addr);
+  }
+
+  Address ConsumeAddress(char** start) {
+    char* end_ptr;
+    Address addr =
+        reinterpret_cast<Address>(strtoul(*start, &end_ptr, 16));  // NOLINT
+    CHECK(HasIndexForAddress(addr));
+    *start = end_ptr;
+    return addr;
+  }
+
+  Interval bounds;
+  // Memory map of entities start addresses.
+  int* entities_map;
+  // An array of code entities.
+  CodeEntityInfo* entities;
+  // Maximal entities count. Actual entities count can be lower,
+  // empty entity slots are pointing to NULL.
+  int max_entities;
+};
+
+}  // namespace
+
+
+typedef void (*ParserBlock)(char* start, char* end, ParseLogResult* result);
+
+static void ParserCycle(
+    char* start, char* end, ParseLogResult* result,
+    ParserBlock block_creation, ParserBlock block_delete,
+    ParserBlock block_move) {
+
+  const char* code_creation = "code-creation,";
+  const char* code_delete = "code-delete,";
+  const char* code_move = "code-move,";
+
+  const char* lazy_compile = "LazyCompile,";
+  const char* script = "Script,";
+  const char* function = "Function,";
+
+  while (start < end) {
+    if (Consume(code_creation, &start)) {
+      if (Consume(lazy_compile, &start)
+          || Consume(script, &start)
+          || Consume(function, &start)) {
+        block_creation(start, end, result);
+      }
+    } else if (Consume(code_delete, &start)) {
+      block_delete(start, end, result);
+    } else if (Consume(code_move, &start)) {
+      block_move(start, end, result);
+    }
+    while (start < end && *start != '\n') ++start;
+    ++start;
+  }
+}
+
+
+static void Pass1CodeCreation(char* start, char* end, ParseLogResult* result) {
+  result->ParseAddress(start);
+  ++result->max_entities;
+}
+
+
+static void Pass1CodeDelete(char* start, char* end, ParseLogResult* result) {
+  result->ParseAddress(start);
+}
+
+
+static void Pass1CodeMove(char* start, char* end, ParseLogResult* result) {
+  result->ParseAddress(start);
+  // Skip old address.
+  while (start < end && *start != ',') ++start;
+  CHECK_GT(end, start);
+  ++start;  // Skip ','.
+  result->ParseAddress(start);
+}
+
+
+static void Pass2CodeCreation(char* start, char* end, ParseLogResult* result) {
+  Address addr = result->ConsumeAddress(&start);
+  CHECK_GT(end, start);
+  ++start;  // Skip ','.
+
+  size_t idx = result->GetIndexForAddress(addr);
+  result->entities_map[idx] = -1;
+  for (int i = 0; i < result->max_entities; ++i) {
+    // Find an empty slot and fill it.
+    if (result->entities[i] == NULL) {
+      result->entities[i] = start;
+      result->entities_map[idx] = i;
+      break;
+    }
+  }
+  // Make sure that a slot was found.
+  CHECK_GE(result->entities_map[idx], 0);
+}
+
+
+static void Pass2CodeDelete(char* start, char* end, ParseLogResult* result) {
+  Address addr = result->ConsumeAddress(&start);
+  size_t idx = result->GetIndexForAddress(addr);
+  // There can be code deletes that are not related to JS code.
+  if (result->entities_map[idx] >= 0) {
+    result->entities[result->entities_map[idx]] = NULL;
+    result->entities_map[idx] = -1;
+  }
+}
+
+
+static void Pass2CodeMove(char* start, char* end, ParseLogResult* result) {
+  Address from_addr = result->ConsumeAddress(&start);
+  CHECK_GT(end, start);
+  ++start;  // Skip ','.
+  Address to_addr = result->ConsumeAddress(&start);
+  CHECK_GT(end, start);
+
+  size_t from_idx = result->GetIndexForAddress(from_addr);
+  size_t to_idx = result->GetIndexForAddress(to_addr);
+  // There can be code moves that are not related to JS code.
+  if (from_idx != to_idx && result->entities_map[from_idx] >= 0) {
+    CHECK_EQ(-1, result->entities_map[to_idx]);
+    result->entities_map[to_idx] = result->entities_map[from_idx];
+    result->entities_map[from_idx] = -1;
+  };
+}
+
+
+static void ParseLog(char* start, char* end, ParseLogResult* result) {
+  // Pass 1: Calculate boundaries of addresses and entities count.
+  ParserCycle(start, end, result,
+              Pass1CodeCreation, Pass1CodeDelete, Pass1CodeMove);
+
+  printf("min_addr: %p, max_addr: %p, entities: %d\n",
+         result->bounds.GetMinAddr(), result->bounds.GetMaxAddr(),
+         result->max_entities);
+
+  result->AllocateEntities();
+
+  // Pass 2: Fill in code entries data.
+  ParserCycle(start, end, result,
+              Pass2CodeCreation, Pass2CodeDelete, Pass2CodeMove);
+}
+
+
+static inline void PrintCodeEntityInfo(CodeEntityInfo entity) {
+  const int max_len = 50;
+  if (entity != NULL) {
+    char* eol = strchr(entity, '\n');
+    int len = eol - entity;
+    len = len <= max_len ? len : max_len;
+    printf("%-*.*s ", max_len, len, entity);
+  } else {
+    printf("%*s", max_len + 1, "");
+  }
+}
+
+
+static void PrintCodeEntitiesInfo(
+    bool is_equal, Address addr,
+    CodeEntityInfo l_entity, CodeEntityInfo r_entity) {
+  printf("%c %p ", is_equal ? ' ' : '*', addr);
+  PrintCodeEntityInfo(l_entity);
+  PrintCodeEntityInfo(r_entity);
+  printf("\n");
+}
+
+
+static inline int StrChrLen(const char* s, char c) {
+  return strchr(s, c) - s;
+}
+
+
+static bool AreFuncSizesEqual(CodeEntityInfo ref_s, CodeEntityInfo new_s) {
+  int ref_len = StrChrLen(ref_s, ',');
+  int new_len = StrChrLen(new_s, ',');
+  return ref_len == new_len && strncmp(ref_s, new_s, ref_len) == 0;
+}
+
+
+static bool AreFuncNamesEqual(CodeEntityInfo ref_s, CodeEntityInfo new_s) {
+  // Skip size.
+  ref_s = strchr(ref_s, ',') + 1;
+  new_s = strchr(new_s, ',') + 1;
+  int ref_len = StrChrLen(ref_s, '\n');
+  int new_len = StrChrLen(new_s, '\n');
+  // If reference is anonymous (""), it's OK to have anything in new.
+  if (ref_len == 2) return true;
+  // A special case for ErrorPrototype. Haven't yet figured out why they
+  // are different.
+  const char* error_prototype = "\"ErrorPrototype";
+  if (IsStringEqualTo(error_prototype, ref_s)
+      && IsStringEqualTo(error_prototype, new_s)) {
+    return true;
+  }
+  // Built-in objects have problems too.
+  const char* built_ins[] = {
+      "\"Boolean\"", "\"Function\"", "\"Number\"",
+      "\"Object\"", "\"Script\"", "\"String\""
+  };
+  for (size_t i = 0; i < sizeof(built_ins) / sizeof(*built_ins); ++i) {
+    if (IsStringEqualTo(built_ins[i], new_s)) {
+      return true;
+    }
+  }
+  return ref_len == new_len && strncmp(ref_s, new_s, ref_len) == 0;
+}
+
+
+static bool AreEntitiesEqual(CodeEntityInfo ref_e, CodeEntityInfo new_e) {
+  if (ref_e == NULL && new_e != NULL) return true;
+  if (ref_e != NULL && new_e != NULL) {
+    return AreFuncSizesEqual(ref_e, new_e) && AreFuncNamesEqual(ref_e, new_e);
+  }
+  if (ref_e != NULL && new_e == NULL) {
+    // args_count entities (argument adapters) are not found by heap traversal,
+    // but they are not needed because they doesn't contain any code.
+    ref_e = strchr(ref_e, ',') + 1;
+    const char* args_count = "\"args_count:";
+    return IsStringEqualTo(args_count, ref_e);
+  }
+  return false;
+}
+
+
+// Test that logging of code create / move / delete events
+// is equivalent to traversal of a resulting heap.
+TEST(EquivalenceOfLoggingAndTraversal) {
+  // This test needs to be run on a "clean" V8 to ensure that snapshot log
+  // is loaded. This is always true when running using tools/test.py because
+  // it launches a new cctest instance for every test. To be sure that launching
+  // cctest manually also works, please be sure that no tests below
+  // are using V8.
+  //
+  // P.S. No, V8 can't be re-initialized after disposal, see include/v8.h.
+  CHECK(!i::V8::IsRunning());
+
+  i::FLAG_logfile = "*";
+  i::FLAG_log = true;
+  i::FLAG_log_code = true;
+
+  // Make sure objects move.
+  bool saved_always_compact = i::FLAG_always_compact;
+  if (!i::FLAG_never_compact) {
+    i::FLAG_always_compact = true;
+  }
+
+  v8::HandleScope scope;
+  v8::Handle<v8::Value> global_object = v8::Handle<v8::Value>();
+  v8::Handle<v8::Context> env = v8::Context::New(
+      0, v8::Handle<v8::ObjectTemplate>(), global_object);
+  env->Enter();
+
+  // Compile and run a function that creates other functions.
+  CompileAndRunScript(
+      "(function f(obj) {\n"
+      "  obj.test =\n"
+      "    (function a(j) { return function b() { return j; } })(100);\n"
+      "})(this);");
+  i::Heap::CollectAllGarbage(false);
+
+  EmbeddedVector<char, 204800> buffer;
+  int log_size;
+  ParseLogResult ref_result;
+
+  // Retrieve the log.
+  {
+    // Make sure that no GCs occur prior to LogCompiledFunctions call.
+    i::AssertNoAllocation no_alloc;
+
+    log_size = GetLogLines(0, &buffer);
+    CHECK_GT(log_size, 0);
+    CHECK_GT(buffer.length(), log_size);
+
+    // Fill a map of compiled code objects.
+    ParseLog(buffer.start(), buffer.start() + log_size, &ref_result);
+  }
+
+  // Iterate heap to find compiled functions, will write to log.
+  i::Logger::LogCompiledFunctions();
+  char* new_log_start = buffer.start() + log_size;
+  const int new_log_size = Logger::GetLogLines(
+      log_size, new_log_start, buffer.length() - log_size);
+  CHECK_GT(new_log_size, 0);
+  CHECK_GT(buffer.length(), log_size + new_log_size);
+
+  // Fill an equivalent map of compiled code objects.
+  ParseLogResult new_result;
+  ParseLog(new_log_start, new_log_start + new_log_size, &new_result);
+
+  // Test their actual equivalence.
+  Interval combined;
+  combined.CloneFrom(&ref_result.bounds);
+  combined.CloneFrom(&new_result.bounds);
+  Interval* iter = &combined;
+  bool results_equal = true;
+
+  while (iter != NULL) {
+    for (Address addr = iter->raw_min_addr();
+         addr <= iter->raw_max_addr(); ++addr) {
+      CodeEntityInfo ref_entity = ref_result.GetEntity(addr);
+      CodeEntityInfo new_entity = new_result.GetEntity(addr);
+      if (ref_entity != NULL || new_entity != NULL) {
+        const bool equal = AreEntitiesEqual(ref_entity, new_entity);
+        if (!equal) results_equal = false;
+        PrintCodeEntitiesInfo(equal, addr, ref_entity, new_entity);
+      }
+    }
+    iter = iter->get_next();
+  }
+  // Make sure that all log data is written prior crash due to CHECK failure.
+  fflush(stdout);
+  CHECK(results_equal);
+
+  env->Exit();
+  Logger::TearDown();
+  i::FLAG_always_compact = saved_always_compact;
+}
+
+#endif  // ENABLE_LOGGING_AND_PROFILING