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