6#if defined(DART_HOST_OS_LINUX)
17#include <sys/resource.h>
19#include <sys/syscall.h>
41#if defined(ARCH_IS_64_BIT)
42#define ElfW(Type) Elf64_##Type
44#define ElfW(Type) Elf32_##Type
55 generate_perf_events_symbols,
57 "Generate events symbols for profiling with perf (disables dual "
61 generate_perf_jitdump,
63 "Generate jitdump file to use with perf-inject (disables dual code "
68#if !defined(DART_PRECOMPILED_RUNTIME)
80class PerfCodeObserver :
public CodeObserver {
82 PerfCodeObserver() : out_file_(nullptr) {
84 if (file_open ==
nullptr) {
87 intptr_t pid = getpid();
88 char* filename = OS::SCreate(
nullptr,
"/tmp/perf-%" Pd ".map", pid);
89 out_file_ = (*file_open)(filename,
true);
95 if ((file_close ==
nullptr) || (out_file_ ==
nullptr)) {
98 (*file_close)(out_file_);
101 virtual bool IsActive()
const {
102 return FLAG_generate_perf_events_symbols && (out_file_ !=
nullptr);
105 virtual void Notify(
const char*
name,
107 uword prologue_offset,
110 const CodeComments* comments) {
112 if ((file_write ==
nullptr) || (out_file_ ==
nullptr)) {
115 const char*
marker = optimized ?
"*" :
"";
117 OS::SCreate(Thread::Current()->zone(),
"%" Px " %" Px " %s%s\n",
base,
120 MutexLocker ml(CodeObservers::mutex());
143class JitDumpCodeObserver :
public CodeObserver {
145 JitDumpCodeObserver() : pid_(getpid()) {
146 char*
const filename = OS::SCreate(
nullptr,
"/tmp/jit-%" Pd ".dump", pid_);
147 const int fd = open(filename, O_CREAT | O_TRUNC | O_RDWR, 0666);
156 const long page_size = sysconf(_SC_PAGESIZE);
157 if (page_size == -1) {
163 mmap(
nullptr, page_size, PROT_READ | PROT_EXEC, MAP_PRIVATE, fd, 0);
164 if (mapped_ ==
nullptr) {
168 mapped_size_ = page_size;
170 out_file_ = fdopen(fd,
"w+");
171 if (out_file_ ==
nullptr) {
178 setvbuf(out_file_,
nullptr, _IOFBF, 2 * MB);
183 FLAG_write_protect_code =
false;
184 FLAG_write_protect_vm_isolate =
false;
186#if !defined(DART_PRECOMPILED_RUNTIME)
188 FLAG_code_comments =
true;
195 ~JitDumpCodeObserver() {
196 if (mapped_ !=
nullptr) {
197 munmap(mapped_, mapped_size_);
201 if (out_file_ !=
nullptr) {
207 virtual bool IsActive()
const {
208 return FLAG_generate_perf_jitdump && (out_file_ !=
nullptr);
211 virtual void Notify(
const char*
name,
213 uword prologue_offset,
216 const CodeComments* comments) {
217 MutexLocker ml(CodeObservers::mutex());
219 const char*
marker = optimized ?
"*" :
"";
220 char*
buffer = OS::SCreate(Thread::Current()->zone(),
"%s%s",
marker,
name);
221 const size_t name_length = strlen(
buffer);
223 WriteDebugInfo(
base, comments);
226 ev.event = BaseEvent::kLoad;
227 ev.size =
sizeof(ev) + (name_length + 1) +
size;
228 ev.time_stamp = OS::GetCurrentMonotonicTicks();
229 ev.process_id = getpid();
230 ev.thread_id = syscall(SYS_gettid);
232 ev.code_address =
base;
234 ev.code_id = code_id_++;
236 WriteFully(&ev,
sizeof(ev));
237 WriteFully(
buffer, name_length + 1);
238 WriteFully(
reinterpret_cast<void*
>(
base), size);
243 const uint32_t
magic = 0x4A695444;
246 uint32_t elf_mach_target;
247 const uint32_t reserved = 0xDEADBEEF;
250 const uint64_t
flags = 0;
267 struct CodeLoadEvent : BaseEvent {
271 uint64_t code_address;
276 struct DebugInfoEvent : BaseEvent {
278 uint64_t entry_count;
282 struct DebugInfoEntry {
289 static uint32_t GetElfMachineArchitecture() {
296#elif TARGET_ARCH_ARM64
298#elif TARGET_ARCH_RISCV32 || TARGET_ARCH_RISCV64
306 void WriteDebugInfo(uword
base,
const CodeComments* comments) {
307 if (comments ==
nullptr || comments->Length() == 0) {
315 char* comments_file_name =
316 OS::SCreate(
nullptr,
"/tmp/jit-%" Pd "-%" Pd ".cmts", pid_, code_id_);
317 const intptr_t filename_length = strlen(comments_file_name);
318 FILE* comments_file = fopen(comments_file_name,
"w");
319 setvbuf(comments_file,
nullptr, _IOFBF, 2 * MB);
323 intptr_t entry_count = 0;
324 for (uint64_t i = 0, len = comments->Length(); i <
len;) {
325 const intptr_t pc_offset = comments->PCOffsetAt(i);
326 while (i < len && comments->PCOffsetAt(i) == pc_offset) {
333 info.event = BaseEvent::kDebugInfo;
334 info.time_stamp = OS::GetCurrentMonotonicTicks();
336 info.entry_count = entry_count;
338 entry_count * (
sizeof(DebugInfoEntry) + filename_length + 1);
339 const int32_t padding = Utils::RoundUp(
info.size, 8) -
info.size;
340 info.size += padding;
345 intptr_t line_number = 0;
346 for (intptr_t i = 0, len = comments->Length(); i <
len;) {
347 const intptr_t pc_offset = comments->PCOffsetAt(i);
348 while (i < len && comments->PCOffsetAt(i) == pc_offset) {
349 line_number += WriteLn(comments_file, comments->CommentAt(i));
352 DebugInfoEntry entry;
353 entry.address =
base + pc_offset +
sizeof(ElfW(Ehdr));
354 entry.line_number = line_number;
356 WriteFully(&entry,
sizeof(entry));
357 WriteFully(comments_file_name, filename_length + 1);
361 const char padding_bytes[8] = {0};
362 WriteFully(padding_bytes, padding);
364 fclose(comments_file);
365 free(comments_file_name);
370 header.elf_mach_target = GetElfMachineArchitecture();
371 header.process_id = getpid();
372 header.time_stamp = OS::GetCurrentTimeMicros();
377 intptr_t WriteLn(FILE* f,
const char* comment) {
381 intptr_t line_count = 1;
382 while ((comment = strstr(comment,
"\n")) !=
nullptr) {
388 void WriteFully(
const void*
buffer,
size_t size) {
389 const char* ptr =
static_cast<const char*
>(
buffer);
391 const size_t written = fwrite(ptr, 1, size, out_file_);
403 FILE* out_file_ =
nullptr;
404 void* mapped_ =
nullptr;
405 long mapped_size_ = 0;
407 intptr_t code_id_ = 0;
415 return static_cast<intptr_t
>(getpid());
418static bool LocalTime(int64_t seconds_since_epoch, tm* tm_result) {
419 time_t seconds =
static_cast<time_t
>(seconds_since_epoch);
420 if (seconds != seconds_since_epoch)
return false;
421 struct tm* error_code = localtime_r(&seconds, tm_result);
422 return error_code !=
nullptr;
427 bool succeeded = LocalTime(seconds_since_epoch, &decomposed);
429 return (succeeded && (decomposed.tm_zone !=
nullptr)) ? decomposed.tm_zone
435 bool succeeded = LocalTime(seconds_since_epoch, &decomposed);
438 return succeeded ?
static_cast<int>(decomposed.tm_gmtoff) : 0;
448 if (gettimeofday(&tv,
nullptr) < 0) {
452 return (
static_cast<int64_t
>(tv.tv_sec) * 1000000) + tv.tv_usec;
457 if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0) {
462 int64_t
result = ts.tv_sec;
480 if (clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ts) != 0) {
484 int64_t
result = ts.tv_sec;
491#if defined(SUPPORT_TIMELINE)
492 if (Timeline::recorder_discards_clock_values())
return -1;
502#if defined(TARGET_ARCH_IA32) || defined(TARGET_ARCH_X64) || \
503 defined(TARGET_ARCH_ARM64) || defined(TARGET_ARCH_RISCV32) || \
504 defined(TARGET_ARCH_RISCV64)
505 const int kMinimumAlignment = 16;
506#elif defined(TARGET_ARCH_ARM)
507 const int kMinimumAlignment = 8;
509#error Unsupported architecture.
511 intptr_t alignment = kMinimumAlignment;
516 ASSERT(alignment >= kMinimumAlignment);
521 return sysconf(_SC_NPROCESSORS_ONLN);
535 req.tv_sec = seconds;
538 int r = nanosleep(&req, &rem);
555 return reinterpret_cast<uintptr_t
>(
556 __builtin_extract_return_addr(__builtin_return_address(0)));
581 va_list measure_args;
582 va_copy(measure_args,
args);
587 if (zone !=
nullptr) {
596 va_copy(print_args,
args);
603 ASSERT(str !=
nullptr && strlen(str) > 0 && value !=
nullptr);
609 }
else if (str[0] ==
'+') {
612 if ((str[i] ==
'0') && (str[i + 1] ==
'x' || str[i + 1] ==
'X') &&
613 (str[i + 2] !=
'\0')) {
620 *
value =
static_cast<int64_t
>(strtoull(str, &endptr,
base));
624 return ((errno == 0) && (endptr != str) && (*endptr == 0));
629 if (FLAG_generate_perf_events_symbols) {
633 if (FLAG_generate_perf_jitdump) {
664 const Image instructions_image(snapshot_instructions);
665 if (
auto*
const image_build_id = instructions_image.build_id()) {
666 return {instructions_image.build_id_length(), image_build_id};
668 const uint8_t* dso_base =
GetAppDSOBase(snapshot_instructions);
669 const ElfW(Ehdr)& elf_header = *
reinterpret_cast<const ElfW(Ehdr)*
>(dso_base);
670 const ElfW(Phdr)*
const phdr_array =
671 reinterpret_cast<const ElfW(Phdr)*
>(dso_base + elf_header.e_phoff);
672 for (intptr_t i = 0; i < elf_header.e_phnum; i++) {
673 const ElfW(Phdr)&
header = phdr_array[i];
674 if (
header.p_type != PT_NOTE)
continue;
675 if ((
header.p_flags & PF_R) != PF_R)
continue;
676 const uint8_t*
const note_addr = dso_base +
header.p_vaddr;
677 const Elf32_Nhdr& note_header =
678 *
reinterpret_cast<const Elf32_Nhdr*
>(note_addr);
679 if (note_header.n_type != NT_GNU_BUILD_ID)
continue;
680 const char*
const note_contents =
681 reinterpret_cast<const char*
>(note_addr +
sizeof(Elf32_Nhdr));
683 if (note_header.n_namesz != strlen(ELF_NOTE_GNU) + 1)
continue;
684 if (strncmp(ELF_NOTE_GNU, note_contents, note_header.n_namesz) == 0) {
685 return {
static_cast<intptr_t
>(note_header.n_descsz),
686 reinterpret_cast<const uint8_t*
>(note_contents +
687 note_header.n_namesz)};
static void info(const char *fmt,...) SK_PRINTF_LIKE(1
static constexpr SkScalar kClose
static const char marker[]
static void Register(CodeObserver *observer)
static DART_NORETURN void Exit(int code)
static int NumberOfAvailableProcessors()
static const char * GetTimeZoneName(int64_t seconds_since_epoch)
static void SleepMicros(int64_t micros)
static int64_t GetCurrentTimeMillis()
static void RegisterCodeObservers()
static int64_t GetCurrentMonotonicMicros()
static void static void PrintErr(const char *format,...) PRINTF_ATTRIBUTE(1
static void Sleep(int64_t millis)
static void Print(const char *format,...) PRINTF_ATTRIBUTE(1
static bool StringToInt64(const char *str, int64_t *value)
static BuildId GetAppBuildId(const uint8_t *snapshot_instructions)
static const uint8_t * GetAppDSOBase(const uint8_t *snapshot_instructions)
static int64_t GetCurrentTimeMicros()
static intptr_t ActivationFrameAlignment()
static void static void static void VFPrint(FILE *stream, const char *format, va_list args)
static DART_NORETURN void Abort()
static char static char * VSCreate(Zone *zone, const char *format, va_list args)
static int64_t GetCurrentThreadCPUMicros()
static uintptr_t GetProgramCounter()
static intptr_t ProcessId()
static void PrepareToAbort()
static int64_t GetCurrentMonotonicFrequency()
static char * SCreate(Zone *zone, const char *format,...) PRINTF_ATTRIBUTE(2
static int64_t GetCurrentMonotonicTicks()
static int64_t GetCurrentMonotonicMicrosForTimeline()
static int GetTimeZoneOffsetInSeconds(int64_t seconds_since_epoch)
static int static int VSNPrint(char *str, size_t size, const char *format, va_list args)
static constexpr bool IsPowerOfTwo(T x)
void(* Dart_FileWriteCallback)(const void *data, intptr_t length, void *stream)
void(* Dart_FileCloseCallback)(void *stream)
void *(* Dart_FileOpenCallback)(const char *name, bool write)
FlutterSemanticsFlag flags
G_BEGIN_DECLS G_MODULE_EXPORT FlValue * args
static const uint8_t buffer[]
uint32_t uint32_t * format
#define DECLARE_FLAG(type, name)
#define DEFINE_FLAG(type, name, default_value, comment)
constexpr intptr_t kMicrosecondsPerMillisecond
constexpr intptr_t kMicrosecondsPerSecond
constexpr intptr_t kNanosecondsPerMicrosecond
void * malloc(size_t size)
constexpr intptr_t kNanosecondsPerSecond
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
static const char header[]