test-log.cc 18.2 KB
Newer Older
1
// Copyright 2006-2009 the V8 project authors. All rights reserved.
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//     * Redistributions of source code must retain the above copyright
//       notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above
//       copyright notice, this list of conditions and the following
//       disclaimer in the documentation and/or other materials provided
//       with the distribution.
//     * Neither the name of Google Inc. nor the names of its
//       contributors may be used to endorse or promote products derived
//       from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 28 29
//
// Tests of logging functions from log.h

30
#ifdef __linux__
31
#include <pthread.h>
32 33
#include <signal.h>
#include <unistd.h>
34
#include <cmath>
35
#endif  // __linux__
36

37
#include "v8.h"
38
#include "log.h"
39
#include "cpu-profiler.h"
40
#include "natives.h"
41
#include "v8threads.h"
42
#include "v8utils.h"
43
#include "cctest.h"
44
#include "vm-state-inl.h"
45

46
using v8::internal::Address;
47
using v8::internal::EmbeddedVector;
48
using v8::internal::Logger;
49
using v8::internal::StrLength;
50

51 52
namespace {

53

54 55
class ScopedLoggerInitializer {
 public:
56
  explicit ScopedLoggerInitializer(bool prof_lazy)
57 58
      : saved_log_(i::FLAG_log),
        saved_prof_lazy_(i::FLAG_prof_lazy),
59 60
        saved_prof_(i::FLAG_prof),
        saved_prof_auto_(i::FLAG_prof_auto),
61 62
        temp_file_(NULL),
        // Need to run this prior to creating the scope.
63
        trick_to_run_init_flags_(init_flags_(prof_lazy)),
64
        scope_(v8::Isolate::GetCurrent()),
65
        env_(v8::Context::New(v8::Isolate::GetCurrent())),
yurys@chromium.org's avatar
yurys@chromium.org committed
66
        logger_(i::Isolate::Current()->logger()) {
67 68 69 70 71
    env_->Enter();
  }

  ~ScopedLoggerInitializer() {
    env_->Exit();
yurys@chromium.org's avatar
yurys@chromium.org committed
72
    logger_->TearDown();
73
    if (temp_file_ != NULL) fclose(temp_file_);
74 75 76
    i::FLAG_prof_lazy = saved_prof_lazy_;
    i::FLAG_prof = saved_prof_;
    i::FLAG_prof_auto = saved_prof_auto_;
77
    i::FLAG_log = saved_log_;
78 79 80 81
  }

  v8::Handle<v8::Context>& env() { return env_; }

yurys@chromium.org's avatar
yurys@chromium.org committed
82 83
  Logger* logger() { return logger_; }

84
  FILE* StopLoggingGetTempFile() {
yurys@chromium.org's avatar
yurys@chromium.org committed
85
    temp_file_ = logger_->TearDown();
86 87 88 89 90 91
    CHECK_NE(NULL, temp_file_);
    fflush(temp_file_);
    rewind(temp_file_);
    return temp_file_;
  }

92
 private:
93
  static bool init_flags_(bool prof_lazy) {
94
    i::FLAG_log = true;
95 96 97
    i::FLAG_prof = true;
    i::FLAG_prof_lazy = prof_lazy;
    i::FLAG_prof_auto = false;
98
    i::FLAG_logfile = i::Log::kLogToTemporaryFile;
99 100 101
    return prof_lazy;
  }

102
  const bool saved_log_;
103 104 105
  const bool saved_prof_lazy_;
  const bool saved_prof_;
  const bool saved_prof_auto_;
106
  FILE* temp_file_;
107 108 109
  const bool trick_to_run_init_flags_;
  v8::HandleScope scope_;
  v8::Handle<v8::Context> env_;
yurys@chromium.org's avatar
yurys@chromium.org committed
110
  Logger* logger_;
111 112 113 114 115 116 117

  DISALLOW_COPY_AND_ASSIGN(ScopedLoggerInitializer);
};

}  // namespace


118 119 120 121 122 123 124
static const char* StrNStr(const char* s1, const char* s2, int n) {
  if (s1[n] == '\0') return strstr(s1, s2);
  i::ScopedVector<char> str(n + 1);
  i::OS::StrNCpy(str, s1, static_cast<size_t>(n));
  str[n] = '\0';
  char* found = strstr(str.start(), s2);
  return found != NULL ? s1 + (found - str.start()) : NULL;
125 126 127 128
}


TEST(ProfLazyMode) {
129
  ScopedLoggerInitializer initialize_logger(true);
yurys@chromium.org's avatar
yurys@chromium.org committed
130
  Logger* logger = initialize_logger.logger();
131

132
  if (!i::V8::UseCrankshaft()) return;
133

yurys@chromium.org's avatar
yurys@chromium.org committed
134
  logger->StringEvent("test-start", "");
135
  CompileRun("var a = (function(x) { return x + 1; })(10);");
yurys@chromium.org's avatar
yurys@chromium.org committed
136
  logger->StringEvent("test-profiler-start", "");
137 138 139 140 141 142 143
  v8::V8::ResumeProfiler();
  CompileRun(
      "var b = (function(x) { return x + 2; })(10);\n"
      "var c = (function(x) { return x + 3; })(10);\n"
      "var d = (function(x) { return x + 4; })(10);\n"
      "var e = (function(x) { return x + 5; })(10);");
  v8::V8::PauseProfiler();
yurys@chromium.org's avatar
yurys@chromium.org committed
144
  logger->StringEvent("test-profiler-stop", "");
145 146
  CompileRun("var f = (function(x) { return x + 6; })(10);");
  // Check that profiling can be resumed again.
yurys@chromium.org's avatar
yurys@chromium.org committed
147
  logger->StringEvent("test-profiler-start-2", "");
148 149 150 151 152 153 154
  v8::V8::ResumeProfiler();
  CompileRun(
      "var g = (function(x) { return x + 7; })(10);\n"
      "var h = (function(x) { return x + 8; })(10);\n"
      "var i = (function(x) { return x + 9; })(10);\n"
      "var j = (function(x) { return x + 10; })(10);");
  v8::V8::PauseProfiler();
yurys@chromium.org's avatar
yurys@chromium.org committed
155 156
  logger->StringEvent("test-profiler-stop-2", "");
  logger->StringEvent("test-stop", "");
157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177

  bool exists = false;
  i::Vector<const char> log(
      i::ReadFile(initialize_logger.StopLoggingGetTempFile(), &exists, true));
  CHECK(exists);

  const char* test_start_position =
      StrNStr(log.start(), "test-start,", log.length());
  CHECK_NE(NULL, test_start_position);
  const char* test_profiler_start_position =
      StrNStr(log.start(), "test-profiler-start,", log.length());
  CHECK_NE(NULL, test_profiler_start_position);
  CHECK_GT(test_profiler_start_position, test_start_position);
  const char* test_profiler_stop_position =
      StrNStr(log.start(), "test-profiler-stop,", log.length());
  CHECK_NE(NULL, test_profiler_stop_position);
  CHECK_GT(test_profiler_stop_position, test_profiler_start_position);
  const char* test_profiler_start_2_position =
      StrNStr(log.start(), "test-profiler-start-2,", log.length());
  CHECK_NE(NULL, test_profiler_start_2_position);
  CHECK_GT(test_profiler_start_2_position, test_profiler_stop_position);
178

179
  // Nothing must be logged until profiling is resumed.
180 181 182 183
  CHECK_EQ(NULL, StrNStr(test_start_position,
                         "code-creation,",
                         static_cast<int>(test_profiler_start_position -
                                          test_start_position)));
184
  // Nothing must be logged while profiling is suspended.
185 186 187 188
  CHECK_EQ(NULL, StrNStr(test_profiler_stop_position,
                         "code-creation,",
                         static_cast<int>(test_profiler_start_2_position -
                                          test_profiler_stop_position)));
189 190 191
}


192 193
// BUG(913). Need to implement support for profiling multiple VM threads.
#if 0
194 195 196 197 198

namespace {

class LoopingThread : public v8::internal::Thread {
 public:
199 200
  explicit LoopingThread(v8::internal::Isolate* isolate)
      : v8::internal::Thread(isolate),
201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238
        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:
239 240
  explicit LoopingJsThread(v8::internal::Isolate* isolate)
      : LoopingThread(isolate) { }
241
  void RunLoop() {
242
    v8::Locker locker;
243 244
    CHECK(i::Isolate::Current() != NULL);
    CHECK_GT(i::Isolate::Current()->thread_manager()->CurrentId(), 0);
245
    SetV8ThreadId();
246 247 248
    while (IsRunning()) {
      v8::HandleScope scope;
      v8::Persistent<v8::Context> context = v8::Context::New();
249 250 251 252
      CHECK(!context.IsEmpty());
      {
        v8::Context::Scope context_scope(context);
        SignalRunning();
253
        CompileRun(
254 255
            "var j; for (var i=0; i<10000; ++i) { j = Math.sin(i); }");
      }
256
      context.Dispose();
257
      i::OS::Sleep(1);
258 259 260 261 262 263 264
    }
  }
};


class LoopingNonJsThread : public LoopingThread {
 public:
265 266
  explicit LoopingNonJsThread(v8::internal::Isolate* isolate)
      : LoopingThread(isolate) { }
267 268 269 270
  void RunLoop() {
    v8::Locker locker;
    v8::Unlocker unlocker;
    // Now thread has V8's id, but will not run VM code.
271 272
    CHECK(i::Isolate::Current() != NULL);
    CHECK_GT(i::Isolate::Current()->thread_manager()->CurrentId(), 0);
273 274 275 276 277 278 279 280 281 282 283 284
    double i = 10;
    SignalRunning();
    while (IsRunning()) {
      i = sin(i);
      i::OS::Sleep(1);
    }
  }
};


class TestSampler : public v8::internal::Sampler {
 public:
285 286
  explicit TestSampler(v8::internal::Isolate* isolate)
      : Sampler(isolate, 0, true, true),
287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313
        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) {
314 315 316
  TestSampler* sampler = NULL;
  {
    v8::Locker locker;
317
    sampler = new TestSampler(v8::internal::Isolate::Current());
318 319 320 321
    sampler->Start();
    CHECK(sampler->IsActive());
  }

322
  LoopingJsThread jsThread(v8::internal::Isolate::Current());
323
  jsThread.Start();
324
  LoopingNonJsThread nonJsThread(v8::internal::Isolate::Current());
325 326
  nonJsThread.Start();

327
  CHECK(!sampler->WasSampleStackCalled());
328 329
  jsThread.WaitForRunning();
  jsThread.SendSigProf();
330 331 332 333
  CHECK(sampler->WaitForTick());
  CHECK(sampler->WasSampleStackCalled());
  sampler->Reset();
  CHECK(!sampler->WasSampleStackCalled());
334 335
  nonJsThread.WaitForRunning();
  nonJsThread.SendSigProf();
336 337 338
  CHECK(!sampler->WaitForTick());
  CHECK(!sampler->WasSampleStackCalled());
  sampler->Stop();
339 340 341 342 343

  jsThread.Stop();
  nonJsThread.Stop();
  jsThread.Join();
  nonJsThread.Join();
344 345

  delete sampler;
346 347 348 349 350
}

#endif  // __linux__


351 352 353 354 355 356 357 358
// Test for issue http://crbug.com/23768 in Chromium.
// Heap can contain scripts with already disposed external sources.
// We need to verify that LogCompiledFunctions doesn't crash on them.
namespace {

class SimpleExternalString : public v8::String::ExternalStringResource {
 public:
  explicit SimpleExternalString(const char* source)
359
      : utf_source_(StrLength(source)) {
360 361 362 363 364 365 366 367 368 369 370 371 372
    for (int i = 0; i < utf_source_.length(); ++i)
      utf_source_[i] = source[i];
  }
  virtual ~SimpleExternalString() {}
  virtual size_t length() const { return utf_source_.length(); }
  virtual const uint16_t* data() const { return utf_source_.start(); }
 private:
  i::ScopedVector<uint16_t> utf_source_;
};

}  // namespace

TEST(Issue23768) {
373
  v8::HandleScope scope(v8::Isolate::GetCurrent());
374
  v8::Handle<v8::Context> env = v8::Context::New(v8::Isolate::GetCurrent());
375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390
  env->Enter();

  SimpleExternalString source_ext_str("(function ext() {})();");
  v8::Local<v8::String> source = v8::String::NewExternal(&source_ext_str);
  // Script needs to have a name in order to trigger InitLineEnds execution.
  v8::Handle<v8::String> origin = v8::String::New("issue-23768-test");
  v8::Handle<v8::Script> evil_script = v8::Script::Compile(source, origin);
  CHECK(!evil_script.IsEmpty());
  CHECK(!evil_script->Run().IsEmpty());
  i::Handle<i::ExternalTwoByteString> i_source(
      i::ExternalTwoByteString::cast(*v8::Utils::OpenHandle(*source)));
  // This situation can happen if source was an external string disposed
  // by its owner.
  i_source->set_resource(NULL);

  // Must not crash.
yurys@chromium.org's avatar
yurys@chromium.org committed
391
  i::Isolate::Current()->logger()->LogCompiledFunctions();
392 393 394
}


395
static void ObjMethod1(const v8::FunctionCallbackInfo<v8::Value>& args) {
396 397
}

398

399
TEST(LogCallbacks) {
400
  ScopedLoggerInitializer initialize_logger(false);
yurys@chromium.org's avatar
yurys@chromium.org committed
401
  Logger* logger = initialize_logger.logger();
402

403 404 405
  v8::Local<v8::FunctionTemplate> obj =
      v8::Local<v8::FunctionTemplate>::New(v8::Isolate::GetCurrent(),
                                           v8::FunctionTemplate::New());
406
  obj->SetClassName(v8_str("Obj"));
407
  v8::Handle<v8::ObjectTemplate> proto = obj->PrototypeTemplate();
408
  v8::Local<v8::Signature> signature = v8::Signature::New(obj);
409
  proto->Set(v8_str("method1"),
410 411 412 413 414
             v8::FunctionTemplate::New(ObjMethod1,
                                       v8::Handle<v8::Value>(),
                                       signature),
             static_cast<v8::PropertyAttribute>(v8::DontDelete));

415
  initialize_logger.env()->Global()->Set(v8_str("Obj"), obj->GetFunction());
416
  CompileRun("Obj.prototype.method1.toString();");
417

yurys@chromium.org's avatar
yurys@chromium.org committed
418
  logger->LogCompiledFunctions();
419

420 421 422 423 424 425
  bool exists = false;
  i::Vector<const char> log(
      i::ReadFile(initialize_logger.StopLoggingGetTempFile(), &exists, true));
  CHECK(exists);

  i::EmbeddedVector<char, 100> ref_data;
426
  i::OS::SNPrintF(ref_data,
427
                  "code-creation,Callback,-2,0x%" V8PRIxPTR ",1,\"method1\"\0",
428 429 430
                  ObjMethod1);

  CHECK_NE(NULL, StrNStr(log.start(), ref_data.start(), log.length()));
431 432 433
}


434 435
static void Prop1Getter(v8::Local<v8::String> property,
                        const v8::PropertyCallbackInfo<v8::Value>& info) {
436 437 438
}

static void Prop1Setter(v8::Local<v8::String> property,
439 440
                        v8::Local<v8::Value> value,
                        const v8::PropertyCallbackInfo<void>& info) {
441 442
}

443 444
static void Prop2Getter(v8::Local<v8::String> property,
                        const v8::PropertyCallbackInfo<v8::Value>& info) {
445 446
}

447

448
TEST(LogAccessorCallbacks) {
449
  ScopedLoggerInitializer initialize_logger(false);
yurys@chromium.org's avatar
yurys@chromium.org committed
450
  Logger* logger = initialize_logger.logger();
451

452 453 454
  v8::Local<v8::FunctionTemplate> obj =
      v8::Local<v8::FunctionTemplate>::New(v8::Isolate::GetCurrent(),
                                           v8::FunctionTemplate::New());
455
  obj->SetClassName(v8_str("Obj"));
456
  v8::Handle<v8::ObjectTemplate> inst = obj->InstanceTemplate();
457 458
  inst->SetAccessor(v8_str("prop1"), Prop1Getter, Prop1Setter);
  inst->SetAccessor(v8_str("prop2"), Prop2Getter);
459

yurys@chromium.org's avatar
yurys@chromium.org committed
460
  logger->LogAccessorCallbacks();
461 462 463 464 465

  bool exists = false;
  i::Vector<const char> log(
      i::ReadFile(initialize_logger.StopLoggingGetTempFile(), &exists, true));
  CHECK(exists);
466 467 468

  EmbeddedVector<char, 100> prop1_getter_record;
  i::OS::SNPrintF(prop1_getter_record,
469
                  "code-creation,Callback,-2,0x%" V8PRIxPTR ",1,\"get prop1\"",
470
                  Prop1Getter);
471 472 473
  CHECK_NE(NULL,
           StrNStr(log.start(), prop1_getter_record.start(), log.length()));

474 475
  EmbeddedVector<char, 100> prop1_setter_record;
  i::OS::SNPrintF(prop1_setter_record,
476
                  "code-creation,Callback,-2,0x%" V8PRIxPTR ",1,\"set prop1\"",
477
                  Prop1Setter);
478 479 480
  CHECK_NE(NULL,
           StrNStr(log.start(), prop1_setter_record.start(), log.length()));

481 482
  EmbeddedVector<char, 100> prop2_getter_record;
  i::OS::SNPrintF(prop2_getter_record,
483
                  "code-creation,Callback,-2,0x%" V8PRIxPTR ",1,\"get prop2\"",
484
                  Prop2Getter);
485 486
  CHECK_NE(NULL,
           StrNStr(log.start(), prop2_getter_record.start(), log.length()));
487
}
488

489

490 491
TEST(IsLoggingPreserved) {
  ScopedLoggerInitializer initialize_logger(false);
yurys@chromium.org's avatar
yurys@chromium.org committed
492
  Logger* logger = initialize_logger.logger();
493

yurys@chromium.org's avatar
yurys@chromium.org committed
494 495 496 497 498
  CHECK(logger->is_logging());
  logger->ResumeProfiler();
  CHECK(logger->is_logging());
  logger->PauseProfiler();
  CHECK(logger->is_logging());
499 500 501
}


502 503
typedef i::NativesCollection<i::TEST> TestSources;

504 505 506

// Test that logging of code create / move events is equivalent to traversal of
// a resulting heap.
507
TEST(EquivalenceOfLoggingAndTraversal) {
508 509 510 511 512 513 514
  // 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.
515
  CHECK(!i::V8::IsRunning());
516

517 518
  // Start with profiling to capture all code events from the beginning.
  ScopedLoggerInitializer initialize_logger(false);
yurys@chromium.org's avatar
yurys@chromium.org committed
519
  Logger* logger = initialize_logger.logger();
520 521

  // Compile and run a function that creates other functions.
522
  CompileRun(
523 524 525
      "(function f(obj) {\n"
      "  obj.test =\n"
      "    (function a(j) { return function b() { return j; } })(100);\n"
526
      "})(this);");
527
  v8::V8::PauseProfiler();
528
  HEAP->CollectAllGarbage(i::Heap::kMakeHeapIterableMask);
yurys@chromium.org's avatar
yurys@chromium.org committed
529
  logger->StringEvent("test-logging-done", "");
530 531

  // Iterate heap to find compiled functions, will write to log.
yurys@chromium.org's avatar
yurys@chromium.org committed
532 533
  logger->LogCompiledFunctions();
  logger->StringEvent("test-traversal-done", "");
534 535 536 537 538 539 540 541

  bool exists = false;
  i::Vector<const char> log(
      i::ReadFile(initialize_logger.StopLoggingGetTempFile(), &exists, true));
  CHECK(exists);
  v8::Handle<v8::String> log_str = v8::String::New(log.start(), log.length());
  initialize_logger.env()->Global()->Set(v8_str("_log"), log_str);

542 543 544 545 546 547 548 549 550 551 552 553 554 555 556
  i::Vector<const unsigned char> source = TestSources::GetScriptsSource();
  v8::Handle<v8::String> source_str = v8::String::New(
      reinterpret_cast<const char*>(source.start()), source.length());
  v8::TryCatch try_catch;
  v8::Handle<v8::Script> script = v8::Script::Compile(source_str, v8_str(""));
  if (script.IsEmpty()) {
    v8::String::Utf8Value exception(try_catch.Exception());
    printf("compile: %s\n", *exception);
    CHECK(false);
  }
  v8::Handle<v8::Value> result = script->Run();
  if (result.IsEmpty()) {
    v8::String::Utf8Value exception(try_catch.Exception());
    printf("run: %s\n", *exception);
    CHECK(false);
557
  }
558
  // The result either be a "true" literal or problem description.
559 560
  if (!result->IsTrue()) {
    v8::Local<v8::String> s = result->ToString();
561
    i::ScopedVector<char> data(s->Utf8Length() + 1);
562
    CHECK_NE(NULL, data.start());
563
    s->WriteUtf8(data.start());
564 565 566 567 568
    printf("%s\n", data.start());
    // Make sure that our output is written prior crash due to CHECK failure.
    fflush(stdout);
    CHECK(false);
  }
569
}