1 // Copyright 2016 the V8 project authors. All rights reserved.
2 // Redistribution and use in source and binary forms, with or without
3 // modification, are permitted provided that the following conditions are
4 // met:
5 //
6 // * Redistributions of source code must retain the above copyright
7 // notice, this list of conditions and the following disclaimer.
8 // * Redistributions in binary form must reproduce the above
9 // copyright notice, this list of conditions and the following
10 // disclaimer in the documentation and/or other materials provided
11 // with the distribution.
12 // * Neither the name of Google Inc. nor the names of its
13 // contributors may be used to endorse or promote products derived
14 // from this software without specific prior written permission.
15 //
16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
28 #include "src/diagnostics/perf-jit.h"
29
30 #include "src/common/assert-scope.h"
31
32 // Only compile the {PerfJitLogger} on Linux.
33 #if V8_OS_LINUX
34
35 #include <fcntl.h>
36 #include <sys/mman.h>
37 #include <unistd.h>
38
39 #include <memory>
40
41 #include "src/base/platform/wrappers.h"
42 #include "src/codegen/assembler.h"
43 #include "src/codegen/source-position-table.h"
44 #include "src/diagnostics/eh-frame.h"
45 #include "src/objects/code-kind.h"
46 #include "src/objects/objects-inl.h"
47 #include "src/objects/shared-function-info.h"
48 #include "src/snapshot/embedded/embedded-data.h"
49 #include "src/utils/ostreams.h"
50
51 #if V8_ENABLE_WEBASSEMBLY
52 #include "src/wasm/wasm-code-manager.h"
53 #endif // V8_ENABLE_WEBASSEMBLY
54
55 namespace v8 {
56 namespace internal {
57
58 struct PerfJitHeader {
59 uint32_t magic_;
60 uint32_t version_;
61 uint32_t size_;
62 uint32_t elf_mach_target_;
63 uint32_t reserved_;
64 uint32_t process_id_;
65 uint64_t time_stamp_;
66 uint64_t flags_;
67
68 static const uint32_t kMagic = 0x4A695444;
69 static const uint32_t kVersion = 1;
70 };
71
72 struct PerfJitBase {
73 enum PerfJitEvent {
74 kLoad = 0,
75 kMove = 1,
76 kDebugInfo = 2,
77 kClose = 3,
78 kUnwindingInfo = 4
79 };
80
81 uint32_t event_;
82 uint32_t size_;
83 uint64_t time_stamp_;
84 };
85
86 struct PerfJitCodeLoad : PerfJitBase {
87 uint32_t process_id_;
88 uint32_t thread_id_;
89 uint64_t vma_;
90 uint64_t code_address_;
91 uint64_t code_size_;
92 uint64_t code_id_;
93 };
94
95 struct PerfJitDebugEntry {
96 uint64_t address_;
97 int line_number_;
98 int column_;
99 // Followed by null-terminated name or \0xFF\0 if same as previous.
100 };
101
102 struct PerfJitCodeDebugInfo : PerfJitBase {
103 uint64_t address_;
104 uint64_t entry_count_;
105 // Followed by entry_count_ instances of PerfJitDebugEntry.
106 };
107
108 struct PerfJitCodeUnwindingInfo : PerfJitBase {
109 uint64_t unwinding_size_;
110 uint64_t eh_frame_hdr_size_;
111 uint64_t mapped_size_;
112 // Followed by size_ - sizeof(PerfJitCodeUnwindingInfo) bytes of data.
113 };
114
115 const char PerfJitLogger::kFilenameFormatString[] = "./jit-%d.dump";
116
117 // Extra padding for the PID in the filename
118 const int PerfJitLogger::kFilenameBufferPadding = 16;
119
120 static const char kStringTerminator[] = {'\0'};
121 static const char kRepeatedNameMarker[] = {'\xff', '\0'};
122
123 base::LazyRecursiveMutex PerfJitLogger::file_mutex_;
124 // The following static variables are protected by PerfJitLogger::file_mutex_.
125 int PerfJitLogger::process_id_ = 0;
126 uint64_t PerfJitLogger::reference_count_ = 0;
127 void* PerfJitLogger::marker_address_ = nullptr;
128 uint64_t PerfJitLogger::code_index_ = 0;
129 FILE* PerfJitLogger::perf_output_handle_ = nullptr;
130
OpenJitDumpFile()131 void PerfJitLogger::OpenJitDumpFile() {
132 // Open the perf JIT dump file.
133 perf_output_handle_ = nullptr;
134
135 int bufferSize = sizeof(kFilenameFormatString) + kFilenameBufferPadding;
136 base::ScopedVector<char> perf_dump_name(bufferSize);
137 int size = SNPrintF(perf_dump_name, kFilenameFormatString, process_id_);
138 CHECK_NE(size, -1);
139
140 int fd = open(perf_dump_name.begin(), O_CREAT | O_TRUNC | O_RDWR, 0666);
141 if (fd == -1) return;
142
143 // If --perf-prof-delete-file is given, unlink the file right after opening
144 // it. This keeps the file handle to the file valid. This only works on Linux,
145 // which is the only platform supported for --perf-prof anyway.
146 if (FLAG_perf_prof_delete_file) CHECK_EQ(0, unlink(perf_dump_name.begin()));
147
148 marker_address_ = OpenMarkerFile(fd);
149 if (marker_address_ == nullptr) return;
150
151 perf_output_handle_ = fdopen(fd, "w+");
152 if (perf_output_handle_ == nullptr) return;
153
154 setvbuf(perf_output_handle_, nullptr, _IOFBF, kLogBufferSize);
155 }
156
CloseJitDumpFile()157 void PerfJitLogger::CloseJitDumpFile() {
158 if (perf_output_handle_ == nullptr) return;
159 base::Fclose(perf_output_handle_);
160 perf_output_handle_ = nullptr;
161 }
162
OpenMarkerFile(int fd)163 void* PerfJitLogger::OpenMarkerFile(int fd) {
164 long page_size = sysconf(_SC_PAGESIZE); // NOLINT(runtime/int)
165 if (page_size == -1) return nullptr;
166
167 // Mmap the file so that there is a mmap record in the perf_data file.
168 //
169 // The map must be PROT_EXEC to ensure it is not ignored by perf record.
170 void* marker_address =
171 mmap(nullptr, page_size, PROT_READ | PROT_EXEC, MAP_PRIVATE, fd, 0);
172 return (marker_address == MAP_FAILED) ? nullptr : marker_address;
173 }
174
CloseMarkerFile(void * marker_address)175 void PerfJitLogger::CloseMarkerFile(void* marker_address) {
176 if (marker_address == nullptr) return;
177 long page_size = sysconf(_SC_PAGESIZE); // NOLINT(runtime/int)
178 if (page_size == -1) return;
179 munmap(marker_address, page_size);
180 }
181
PerfJitLogger(Isolate * isolate)182 PerfJitLogger::PerfJitLogger(Isolate* isolate) : CodeEventLogger(isolate) {
183 base::LockGuard<base::RecursiveMutex> guard_file(file_mutex_.Pointer());
184 process_id_ = base::OS::GetCurrentProcessId();
185
186 reference_count_++;
187 // If this is the first logger, open the file and write the header.
188 if (reference_count_ == 1) {
189 OpenJitDumpFile();
190 if (perf_output_handle_ == nullptr) return;
191 LogWriteHeader();
192 }
193 }
194
~PerfJitLogger()195 PerfJitLogger::~PerfJitLogger() {
196 base::LockGuard<base::RecursiveMutex> guard_file(file_mutex_.Pointer());
197
198 reference_count_--;
199 // If this was the last logger, close the file.
200 if (reference_count_ == 0) {
201 CloseJitDumpFile();
202 }
203 }
204
GetTimestamp()205 uint64_t PerfJitLogger::GetTimestamp() {
206 struct timespec ts;
207 int result = clock_gettime(CLOCK_MONOTONIC, &ts);
208 DCHECK_EQ(0, result);
209 USE(result);
210 static const uint64_t kNsecPerSec = 1000000000;
211 return (ts.tv_sec * kNsecPerSec) + ts.tv_nsec;
212 }
213
LogRecordedBuffer(Handle<AbstractCode> abstract_code,MaybeHandle<SharedFunctionInfo> maybe_shared,const char * name,int length)214 void PerfJitLogger::LogRecordedBuffer(
215 Handle<AbstractCode> abstract_code,
216 MaybeHandle<SharedFunctionInfo> maybe_shared, const char* name,
217 int length) {
218 if (FLAG_perf_basic_prof_only_functions && !CodeKindIsJSFunction(abstract_code->kind())) {
219 return;
220 }
221
222 base::LockGuard<base::RecursiveMutex> guard_file(file_mutex_.Pointer());
223
224 if (perf_output_handle_ == nullptr) return;
225
226 // We only support non-interpreted functions.
227 if (!abstract_code->IsCode()) return;
228 Handle<Code> code = Handle<Code>::cast(abstract_code);
229 DCHECK(code->raw_instruction_start() == code->address() + Code::kHeaderSize);
230
231 // Debug info has to be emitted first.
232 Handle<SharedFunctionInfo> shared;
233 if (FLAG_perf_prof && maybe_shared.ToHandle(&shared)) {
234 // TODO(herhut): This currently breaks for js2wasm/wasm2js functions.
235 if (code->kind() != CodeKind::JS_TO_WASM_FUNCTION &&
236 code->kind() != CodeKind::WASM_TO_JS_FUNCTION) {
237 LogWriteDebugInfo(code, shared);
238 }
239 }
240
241 const char* code_name = name;
242 uint8_t* code_pointer = reinterpret_cast<uint8_t*>(code->InstructionStart());
243
244 // Unwinding info comes right after debug info.
245 if (FLAG_perf_prof_unwinding_info) LogWriteUnwindingInfo(*code);
246
247 WriteJitCodeLoadEntry(code_pointer, code->InstructionSize(), code_name,
248 length);
249 }
250
251 #if V8_ENABLE_WEBASSEMBLY
LogRecordedBuffer(const wasm::WasmCode * code,const char * name,int length)252 void PerfJitLogger::LogRecordedBuffer(const wasm::WasmCode* code,
253 const char* name, int length) {
254 base::LockGuard<base::RecursiveMutex> guard_file(file_mutex_.Pointer());
255
256 if (perf_output_handle_ == nullptr) return;
257
258 if (FLAG_perf_prof_annotate_wasm) LogWriteDebugInfo(code);
259
260 WriteJitCodeLoadEntry(code->instructions().begin(),
261 code->instructions().length(), name, length);
262 }
263 #endif // V8_ENABLE_WEBASSEMBLY
264
WriteJitCodeLoadEntry(const uint8_t * code_pointer,uint32_t code_size,const char * name,int name_length)265 void PerfJitLogger::WriteJitCodeLoadEntry(const uint8_t* code_pointer,
266 uint32_t code_size, const char* name,
267 int name_length) {
268 PerfJitCodeLoad code_load;
269 code_load.event_ = PerfJitCodeLoad::kLoad;
270 code_load.size_ = sizeof(code_load) + name_length + 1 + code_size;
271 code_load.time_stamp_ = GetTimestamp();
272 code_load.process_id_ = static_cast<uint32_t>(process_id_);
273 code_load.thread_id_ = static_cast<uint32_t>(base::OS::GetCurrentThreadId());
274 code_load.vma_ = reinterpret_cast<uint64_t>(code_pointer);
275 code_load.code_address_ = reinterpret_cast<uint64_t>(code_pointer);
276 code_load.code_size_ = code_size;
277 code_load.code_id_ = code_index_;
278
279 code_index_++;
280
281 LogWriteBytes(reinterpret_cast<const char*>(&code_load), sizeof(code_load));
282 LogWriteBytes(name, name_length);
283 LogWriteBytes(kStringTerminator, sizeof(kStringTerminator));
284 LogWriteBytes(reinterpret_cast<const char*>(code_pointer), code_size);
285 }
286
287 namespace {
288
289 constexpr char kUnknownScriptNameString[] = "<unknown>";
290 constexpr size_t kUnknownScriptNameStringLen =
291 arraysize(kUnknownScriptNameString) - 1;
292
293 namespace {
GetScriptName(Object maybeScript,std::unique_ptr<char[]> * storage,const DisallowGarbageCollection & no_gc)294 base::Vector<const char> GetScriptName(Object maybeScript,
295 std::unique_ptr<char[]>* storage,
296 const DisallowGarbageCollection& no_gc) {
297 if (maybeScript.IsScript()) {
298 Object name_or_url = Script::cast(maybeScript).GetNameOrSourceURL();
299 if (name_or_url.IsSeqOneByteString()) {
300 SeqOneByteString str = SeqOneByteString::cast(name_or_url);
301 return {reinterpret_cast<char*>(str.GetChars(no_gc)),
302 static_cast<size_t>(str.length())};
303 } else if (name_or_url.IsString()) {
304 int length;
305 *storage = String::cast(name_or_url)
306 .ToCString(DISALLOW_NULLS, FAST_STRING_TRAVERSAL, &length);
307 return {storage->get(), static_cast<size_t>(length)};
308 }
309 }
310 return {kUnknownScriptNameString, kUnknownScriptNameStringLen};
311 }
312
313 } // namespace
314
GetSourcePositionInfo(Handle<Code> code,Handle<SharedFunctionInfo> function,SourcePosition pos)315 SourcePositionInfo GetSourcePositionInfo(Handle<Code> code,
316 Handle<SharedFunctionInfo> function,
317 SourcePosition pos) {
318 DisallowGarbageCollection disallow;
319 if (code->is_turbofanned()) {
320 return pos.FirstInfo(code);
321 } else {
322 return SourcePositionInfo(pos, function);
323 }
324 }
325
326 } // namespace
327
LogWriteDebugInfo(Handle<Code> code,Handle<SharedFunctionInfo> shared)328 void PerfJitLogger::LogWriteDebugInfo(Handle<Code> code,
329 Handle<SharedFunctionInfo> shared) {
330 // Line ends of all scripts have been initialized prior to this.
331 DisallowGarbageCollection no_gc;
332 // The WasmToJS wrapper stubs have source position entries.
333 if (!shared->HasSourceCode()) return;
334
335 PerfJitCodeDebugInfo debug_info;
336 uint32_t size = sizeof(debug_info);
337
338 ByteArray source_position_table = code->SourcePositionTable(*shared);
339 // Compute the entry count and get the names of all scripts.
340 // Avoid additional work if the script name is repeated. Multiple script
341 // names only occur for cross-script inlining.
342 uint32_t entry_count = 0;
343 Object last_script = Smi::zero();
344 std::vector<base::Vector<const char>> script_names;
345 for (SourcePositionTableIterator iterator(source_position_table);
346 !iterator.done(); iterator.Advance()) {
347 SourcePositionInfo info(
348 GetSourcePositionInfo(code, shared, iterator.source_position()));
349 Object current_script = *info.script;
350 if (current_script != last_script) {
351 std::unique_ptr<char[]> name_storage;
352 auto name = GetScriptName(shared->script(), &name_storage, no_gc);
353 script_names.push_back(name);
354 // Add the size of the name after each entry.
355 size += name.size() + sizeof(kStringTerminator);
356 last_script = current_script;
357 } else {
358 size += sizeof(kRepeatedNameMarker);
359 }
360 entry_count++;
361 }
362 if (entry_count == 0) return;
363
364 debug_info.event_ = PerfJitCodeLoad::kDebugInfo;
365 debug_info.time_stamp_ = GetTimestamp();
366 debug_info.address_ = code->InstructionStart();
367 debug_info.entry_count_ = entry_count;
368
369 // Add the sizes of fixed parts of entries.
370 size += entry_count * sizeof(PerfJitDebugEntry);
371
372 int padding = ((size + 7) & (~7)) - size;
373 debug_info.size_ = size + padding;
374 LogWriteBytes(reinterpret_cast<const char*>(&debug_info), sizeof(debug_info));
375
376 Address code_start = code->InstructionStart();
377
378 last_script = Smi::zero();
379 int script_names_index = 0;
380 for (SourcePositionTableIterator iterator(source_position_table);
381 !iterator.done(); iterator.Advance()) {
382 SourcePositionInfo info(
383 GetSourcePositionInfo(code, shared, iterator.source_position()));
384 PerfJitDebugEntry entry;
385 // The entry point of the function will be placed straight after the ELF
386 // header when processed by "perf inject". Adjust the position addresses
387 // accordingly.
388 entry.address_ = code_start + iterator.code_offset() + kElfHeaderSize;
389 entry.line_number_ = info.line + 1;
390 entry.column_ = info.column + 1;
391 LogWriteBytes(reinterpret_cast<const char*>(&entry), sizeof(entry));
392 Object current_script = *info.script;
393 if (current_script != last_script) {
394 auto name_string = script_names[script_names_index];
395 LogWriteBytes(name_string.begin(),
396 static_cast<uint32_t>(name_string.size()));
397 LogWriteBytes(kStringTerminator, sizeof(kStringTerminator));
398 script_names_index++;
399 last_script = current_script;
400 } else {
401 // Use the much shorter kRepeatedNameMarker for repeated names.
402 LogWriteBytes(kRepeatedNameMarker, sizeof(kRepeatedNameMarker));
403 }
404 }
405 char padding_bytes[8] = {0};
406 LogWriteBytes(padding_bytes, padding);
407 }
408
409 #if V8_ENABLE_WEBASSEMBLY
LogWriteDebugInfo(const wasm::WasmCode * code)410 void PerfJitLogger::LogWriteDebugInfo(const wasm::WasmCode* code) {
411 wasm::WasmModuleSourceMap* source_map =
412 code->native_module()->GetWasmSourceMap();
413 wasm::WireBytesRef code_ref =
414 code->native_module()->module()->functions[code->index()].code;
415 uint32_t code_offset = code_ref.offset();
416 uint32_t code_end_offset = code_ref.end_offset();
417
418 uint32_t entry_count = 0;
419 uint32_t size = 0;
420
421 if (!source_map || !source_map->IsValid() ||
422 !source_map->HasSource(code_offset, code_end_offset)) {
423 return;
424 }
425
426 for (SourcePositionTableIterator iterator(code->source_positions());
427 !iterator.done(); iterator.Advance()) {
428 uint32_t offset = iterator.source_position().ScriptOffset() + code_offset;
429 if (!source_map->HasValidEntry(code_offset, offset)) continue;
430 entry_count++;
431 size += source_map->GetFilename(offset).size() + 1;
432 }
433
434 if (entry_count == 0) return;
435
436 PerfJitCodeDebugInfo debug_info;
437
438 debug_info.event_ = PerfJitCodeLoad::kDebugInfo;
439 debug_info.time_stamp_ = GetTimestamp();
440 debug_info.address_ =
441 reinterpret_cast<uintptr_t>(code->instructions().begin());
442 debug_info.entry_count_ = entry_count;
443
444 size += sizeof(debug_info);
445 // Add the sizes of fixed parts of entries.
446 size += entry_count * sizeof(PerfJitDebugEntry);
447
448 int padding = ((size + 7) & (~7)) - size;
449 debug_info.size_ = size + padding;
450 LogWriteBytes(reinterpret_cast<const char*>(&debug_info), sizeof(debug_info));
451
452 uintptr_t code_begin =
453 reinterpret_cast<uintptr_t>(code->instructions().begin());
454
455 for (SourcePositionTableIterator iterator(code->source_positions());
456 !iterator.done(); iterator.Advance()) {
457 uint32_t offset = iterator.source_position().ScriptOffset() + code_offset;
458 if (!source_map->HasValidEntry(code_offset, offset)) continue;
459 PerfJitDebugEntry entry;
460 // The entry point of the function will be placed straight after the ELF
461 // header when processed by "perf inject". Adjust the position addresses
462 // accordingly.
463 entry.address_ = code_begin + iterator.code_offset() + kElfHeaderSize;
464 entry.line_number_ =
465 static_cast<int>(source_map->GetSourceLine(offset)) + 1;
466 entry.column_ = 1;
467 LogWriteBytes(reinterpret_cast<const char*>(&entry), sizeof(entry));
468 std::string name_string = source_map->GetFilename(offset);
469 LogWriteBytes(name_string.c_str(), static_cast<int>(name_string.size()));
470 LogWriteBytes(kStringTerminator, sizeof(kStringTerminator));
471 }
472
473 char padding_bytes[8] = {0};
474 LogWriteBytes(padding_bytes, padding);
475 }
476 #endif // V8_ENABLE_WEBASSEMBLY
477
LogWriteUnwindingInfo(Code code)478 void PerfJitLogger::LogWriteUnwindingInfo(Code code) {
479 PerfJitCodeUnwindingInfo unwinding_info_header;
480 unwinding_info_header.event_ = PerfJitCodeLoad::kUnwindingInfo;
481 unwinding_info_header.time_stamp_ = GetTimestamp();
482 unwinding_info_header.eh_frame_hdr_size_ = EhFrameConstants::kEhFrameHdrSize;
483
484 if (code.has_unwinding_info()) {
485 unwinding_info_header.unwinding_size_ = code.unwinding_info_size();
486 unwinding_info_header.mapped_size_ = unwinding_info_header.unwinding_size_;
487 } else {
488 unwinding_info_header.unwinding_size_ = EhFrameConstants::kEhFrameHdrSize;
489 unwinding_info_header.mapped_size_ = 0;
490 }
491
492 int content_size = static_cast<int>(sizeof(unwinding_info_header) +
493 unwinding_info_header.unwinding_size_);
494 int padding_size = RoundUp(content_size, 8) - content_size;
495 unwinding_info_header.size_ = content_size + padding_size;
496
497 LogWriteBytes(reinterpret_cast<const char*>(&unwinding_info_header),
498 sizeof(unwinding_info_header));
499
500 if (code.has_unwinding_info()) {
501 LogWriteBytes(reinterpret_cast<const char*>(code.unwinding_info_start()),
502 code.unwinding_info_size());
503 } else {
504 OFStream perf_output_stream(perf_output_handle_);
505 EhFrameWriter::WriteEmptyEhFrame(perf_output_stream);
506 }
507
508 char padding_bytes[] = "\0\0\0\0\0\0\0\0";
509 DCHECK_LT(padding_size, static_cast<int>(sizeof(padding_bytes)));
510 LogWriteBytes(padding_bytes, static_cast<int>(padding_size));
511 }
512
CodeMoveEvent(AbstractCode from,AbstractCode to)513 void PerfJitLogger::CodeMoveEvent(AbstractCode from, AbstractCode to) {
514 // We may receive a CodeMove event if a BytecodeArray object moves. Otherwise
515 // code relocation is not supported.
516 CHECK(from.IsBytecodeArray());
517 }
518
LogWriteBytes(const char * bytes,int size)519 void PerfJitLogger::LogWriteBytes(const char* bytes, int size) {
520 size_t rv = fwrite(bytes, 1, size, perf_output_handle_);
521 DCHECK(static_cast<size_t>(size) == rv);
522 USE(rv);
523 }
524
LogWriteHeader()525 void PerfJitLogger::LogWriteHeader() {
526 DCHECK_NOT_NULL(perf_output_handle_);
527 PerfJitHeader header;
528
529 header.magic_ = PerfJitHeader::kMagic;
530 header.version_ = PerfJitHeader::kVersion;
531 header.size_ = sizeof(header);
532 header.elf_mach_target_ = GetElfMach();
533 header.reserved_ = 0xDEADBEEF;
534 header.process_id_ = process_id_;
535 header.time_stamp_ =
536 static_cast<uint64_t>(V8::GetCurrentPlatform()->CurrentClockTimeMillis() *
537 base::Time::kMicrosecondsPerMillisecond);
538 header.flags_ = 0;
539
540 LogWriteBytes(reinterpret_cast<const char*>(&header), sizeof(header));
541 }
542
543 } // namespace internal
544 } // namespace v8
545
546 #endif // V8_OS_LINUX
547