10b57cec5SDimitry Andric /*===- InstrProfilingPlatformFuchsia.c - Profile data Fuchsia platform ----===*\ 20b57cec5SDimitry Andric |* 30b57cec5SDimitry Andric |* Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 40b57cec5SDimitry Andric |* See https://llvm.org/LICENSE.txt for license information. 50b57cec5SDimitry Andric |* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 60b57cec5SDimitry Andric |* 70b57cec5SDimitry Andric \*===----------------------------------------------------------------------===*/ 80b57cec5SDimitry Andric /* 90b57cec5SDimitry Andric * This file implements the profiling runtime for Fuchsia and defines the 100b57cec5SDimitry Andric * shared profile runtime interface. Each module (executable or DSO) statically 110b57cec5SDimitry Andric * links in the whole profile runtime to satisfy the calls from its 120b57cec5SDimitry Andric * instrumented code. Several modules in the same program might be separately 130b57cec5SDimitry Andric * compiled and even use different versions of the instrumentation ABI and data 140b57cec5SDimitry Andric * format. All they share in common is the VMO and the offset, which live in 150b57cec5SDimitry Andric * exported globals so that exactly one definition will be shared across all 160b57cec5SDimitry Andric * modules. Each module has its own independent runtime that registers its own 170b57cec5SDimitry Andric * atexit hook to append its own data into the shared VMO which is published 180b57cec5SDimitry Andric * via the data sink hook provided by Fuchsia's dynamic linker. 190b57cec5SDimitry Andric */ 200b57cec5SDimitry Andric 210b57cec5SDimitry Andric #if defined(__Fuchsia__) 220b57cec5SDimitry Andric 230b57cec5SDimitry Andric #include <inttypes.h> 240b57cec5SDimitry Andric #include <stdarg.h> 250b57cec5SDimitry Andric #include <stdbool.h> 260b57cec5SDimitry Andric #include <stdlib.h> 270b57cec5SDimitry Andric 280b57cec5SDimitry Andric #include <zircon/process.h> 290b57cec5SDimitry Andric #include <zircon/sanitizer.h> 3068d75effSDimitry Andric #include <zircon/status.h> 310b57cec5SDimitry Andric #include <zircon/syscalls.h> 320b57cec5SDimitry Andric 330b57cec5SDimitry Andric #include "InstrProfiling.h" 340b57cec5SDimitry Andric #include "InstrProfilingInternal.h" 350b57cec5SDimitry Andric #include "InstrProfilingUtil.h" 360b57cec5SDimitry Andric 37fe6060f1SDimitry Andric /* This variable is an external reference to symbol defined by the compiler. */ 38fe6060f1SDimitry Andric COMPILER_RT_VISIBILITY extern intptr_t INSTR_PROF_PROFILE_COUNTER_BIAS_VAR; 39fe6060f1SDimitry Andric 405ffd83dbSDimitry Andric COMPILER_RT_VISIBILITY unsigned lprofProfileDumped() { 415ffd83dbSDimitry Andric return 1; 425ffd83dbSDimitry Andric } 435ffd83dbSDimitry Andric COMPILER_RT_VISIBILITY void lprofSetProfileDumped(unsigned Value) {} 445ffd83dbSDimitry Andric 450b57cec5SDimitry Andric static const char ProfileSinkName[] = "llvm-profile"; 460b57cec5SDimitry Andric 470b57cec5SDimitry Andric static inline void lprofWrite(const char *fmt, ...) { 480b57cec5SDimitry Andric char s[256]; 490b57cec5SDimitry Andric 500b57cec5SDimitry Andric va_list ap; 510b57cec5SDimitry Andric va_start(ap, fmt); 520b57cec5SDimitry Andric int ret = vsnprintf(s, sizeof(s), fmt, ap); 530b57cec5SDimitry Andric va_end(ap); 540b57cec5SDimitry Andric 55349cc55cSDimitry Andric __sanitizer_log_write(s, ret); 560b57cec5SDimitry Andric } 570b57cec5SDimitry Andric 585ffd83dbSDimitry Andric struct lprofVMOWriterCtx { 595ffd83dbSDimitry Andric /* VMO that contains the profile data for this module. */ 605ffd83dbSDimitry Andric zx_handle_t Vmo; 615ffd83dbSDimitry Andric /* Current offset within the VMO where data should be written next. */ 625ffd83dbSDimitry Andric uint64_t Offset; 635ffd83dbSDimitry Andric }; 640b57cec5SDimitry Andric 6568d75effSDimitry Andric static uint32_t lprofVMOWriter(ProfDataWriter *This, ProfDataIOVec *IOVecs, 6668d75effSDimitry Andric uint32_t NumIOVecs) { 675ffd83dbSDimitry Andric struct lprofVMOWriterCtx *Ctx = (struct lprofVMOWriterCtx *)This->WriterCtx; 685ffd83dbSDimitry Andric 690b57cec5SDimitry Andric /* Compute the total length of data to be written. */ 700b57cec5SDimitry Andric size_t Length = 0; 710b57cec5SDimitry Andric for (uint32_t I = 0; I < NumIOVecs; I++) 720b57cec5SDimitry Andric Length += IOVecs[I].ElmSize * IOVecs[I].NumElm; 730b57cec5SDimitry Andric 740b57cec5SDimitry Andric /* Resize the VMO to ensure there's sufficient space for the data. */ 755ffd83dbSDimitry Andric zx_status_t Status = _zx_vmo_set_size(Ctx->Vmo, Ctx->Offset + Length); 760b57cec5SDimitry Andric if (Status != ZX_OK) 770b57cec5SDimitry Andric return -1; 780b57cec5SDimitry Andric 790b57cec5SDimitry Andric /* Copy the data into VMO. */ 800b57cec5SDimitry Andric for (uint32_t I = 0; I < NumIOVecs; I++) { 810b57cec5SDimitry Andric size_t Length = IOVecs[I].ElmSize * IOVecs[I].NumElm; 820b57cec5SDimitry Andric if (IOVecs[I].Data) { 835ffd83dbSDimitry Andric Status = _zx_vmo_write(Ctx->Vmo, IOVecs[I].Data, Ctx->Offset, Length); 840b57cec5SDimitry Andric if (Status != ZX_OK) 850b57cec5SDimitry Andric return -1; 86480093f4SDimitry Andric } else if (IOVecs[I].UseZeroPadding) { 87480093f4SDimitry Andric /* Resizing the VMO should zero fill. */ 880b57cec5SDimitry Andric } 895ffd83dbSDimitry Andric Ctx->Offset += Length; 900b57cec5SDimitry Andric } 910b57cec5SDimitry Andric 925ffd83dbSDimitry Andric /* Record the profile size as a property of the VMO. */ 935ffd83dbSDimitry Andric _zx_object_set_property(Ctx->Vmo, ZX_PROP_VMO_CONTENT_SIZE, &Ctx->Offset, 945ffd83dbSDimitry Andric sizeof(Ctx->Offset)); 955ffd83dbSDimitry Andric 960b57cec5SDimitry Andric return 0; 970b57cec5SDimitry Andric } 980b57cec5SDimitry Andric 995ffd83dbSDimitry Andric static void initVMOWriter(ProfDataWriter *This, struct lprofVMOWriterCtx *Ctx) { 1000b57cec5SDimitry Andric This->Write = lprofVMOWriter; 1015ffd83dbSDimitry Andric This->WriterCtx = Ctx; 1020b57cec5SDimitry Andric } 1030b57cec5SDimitry Andric 1045ffd83dbSDimitry Andric /* This method is invoked by the runtime initialization hook 1055ffd83dbSDimitry Andric * InstrProfilingRuntime.o if it is linked in. */ 1065ffd83dbSDimitry Andric COMPILER_RT_VISIBILITY 1075ffd83dbSDimitry Andric void __llvm_profile_initialize(void) { 1080b57cec5SDimitry Andric /* Check if there is llvm/runtime version mismatch. */ 1090b57cec5SDimitry Andric if (GET_VERSION(__llvm_profile_get_version()) != INSTR_PROF_RAW_VERSION) { 11068d75effSDimitry Andric lprofWrite("LLVM Profile: runtime and instrumentation version mismatch: " 1110b57cec5SDimitry Andric "expected %d, but got %d\n", 1120b57cec5SDimitry Andric INSTR_PROF_RAW_VERSION, 1130b57cec5SDimitry Andric (int)GET_VERSION(__llvm_profile_get_version())); 1145ffd83dbSDimitry Andric return; 1150b57cec5SDimitry Andric } 1160b57cec5SDimitry Andric 1175ffd83dbSDimitry Andric const __llvm_profile_data *DataBegin = __llvm_profile_begin_data(); 1185ffd83dbSDimitry Andric const __llvm_profile_data *DataEnd = __llvm_profile_end_data(); 119*04eeddc0SDimitry Andric const char *CountersBegin = __llvm_profile_begin_counters(); 120*04eeddc0SDimitry Andric const char *CountersEnd = __llvm_profile_end_counters(); 1215ffd83dbSDimitry Andric const uint64_t DataSize = __llvm_profile_get_data_size(DataBegin, DataEnd); 122349cc55cSDimitry Andric const uint64_t CountersOffset = 123*04eeddc0SDimitry Andric sizeof(__llvm_profile_header) + __llvm_write_binary_ids(NULL) + DataSize; 124*04eeddc0SDimitry Andric uint64_t CountersSize = 125*04eeddc0SDimitry Andric __llvm_profile_get_counters_size(CountersBegin, CountersEnd); 126fe6060f1SDimitry Andric 127fe6060f1SDimitry Andric /* Don't publish a VMO if there are no counters. */ 128fe6060f1SDimitry Andric if (!CountersSize) 129fe6060f1SDimitry Andric return; 1305ffd83dbSDimitry Andric 1315ffd83dbSDimitry Andric zx_status_t Status; 1325ffd83dbSDimitry Andric 133fe6060f1SDimitry Andric /* Create a VMO to hold the profile data. */ 1345ffd83dbSDimitry Andric zx_handle_t Vmo = ZX_HANDLE_INVALID; 1355ffd83dbSDimitry Andric Status = _zx_vmo_create(0, ZX_VMO_RESIZABLE, &Vmo); 1365ffd83dbSDimitry Andric if (Status != ZX_OK) { 1375ffd83dbSDimitry Andric lprofWrite("LLVM Profile: cannot create VMO: %s\n", 1385ffd83dbSDimitry Andric _zx_status_get_string(Status)); 1395ffd83dbSDimitry Andric return; 1405ffd83dbSDimitry Andric } 1415ffd83dbSDimitry Andric 1425ffd83dbSDimitry Andric /* Give the VMO a name that includes the module signature. */ 1435ffd83dbSDimitry Andric char VmoName[ZX_MAX_NAME_LEN]; 1445ffd83dbSDimitry Andric snprintf(VmoName, sizeof(VmoName), "%" PRIu64 ".profraw", 1455ffd83dbSDimitry Andric lprofGetLoadModuleSignature()); 1465ffd83dbSDimitry Andric _zx_object_set_property(Vmo, ZX_PROP_NAME, VmoName, strlen(VmoName)); 1475ffd83dbSDimitry Andric 1480b57cec5SDimitry Andric /* Write the profile data into the mapped region. */ 1490b57cec5SDimitry Andric ProfDataWriter VMOWriter; 1505ffd83dbSDimitry Andric struct lprofVMOWriterCtx Ctx = {.Vmo = Vmo, .Offset = 0}; 1515ffd83dbSDimitry Andric initVMOWriter(&VMOWriter, &Ctx); 1525ffd83dbSDimitry Andric if (lprofWriteData(&VMOWriter, 0, 0) != 0) { 1535ffd83dbSDimitry Andric lprofWrite("LLVM Profile: failed to write data\n"); 1545ffd83dbSDimitry Andric _zx_handle_close(Vmo); 1555ffd83dbSDimitry Andric return; 1560b57cec5SDimitry Andric } 1570b57cec5SDimitry Andric 1585ffd83dbSDimitry Andric uint64_t Len = 0; 1595ffd83dbSDimitry Andric Status = _zx_vmo_get_size(Vmo, &Len); 1605ffd83dbSDimitry Andric if (Status != ZX_OK) { 1615ffd83dbSDimitry Andric lprofWrite("LLVM Profile: failed to get the VMO size: %s\n", 1625ffd83dbSDimitry Andric _zx_status_get_string(Status)); 1635ffd83dbSDimitry Andric _zx_handle_close(Vmo); 1645ffd83dbSDimitry Andric return; 1650b57cec5SDimitry Andric } 1660b57cec5SDimitry Andric 1675ffd83dbSDimitry Andric uintptr_t Mapping; 1685ffd83dbSDimitry Andric Status = 1695ffd83dbSDimitry Andric _zx_vmar_map(_zx_vmar_root_self(), ZX_VM_PERM_READ | ZX_VM_PERM_WRITE, 0, 1705ffd83dbSDimitry Andric Vmo, 0, Len, &Mapping); 1715ffd83dbSDimitry Andric if (Status != ZX_OK) { 1725ffd83dbSDimitry Andric lprofWrite("LLVM Profile: failed to map the VMO: %s\n", 1735ffd83dbSDimitry Andric _zx_status_get_string(Status)); 1745ffd83dbSDimitry Andric _zx_handle_close(Vmo); 1755ffd83dbSDimitry Andric return; 1765ffd83dbSDimitry Andric } 1770b57cec5SDimitry Andric 1785ffd83dbSDimitry Andric /* Publish the VMO which contains profile data to the system. Note that this 1795ffd83dbSDimitry Andric * also consumes the VMO handle. */ 1805ffd83dbSDimitry Andric __sanitizer_publish_data(ProfileSinkName, Vmo); 1810b57cec5SDimitry Andric 1825ffd83dbSDimitry Andric /* Update the profile fields based on the current mapping. */ 183fe6060f1SDimitry Andric INSTR_PROF_PROFILE_COUNTER_BIAS_VAR = 184fe6060f1SDimitry Andric (intptr_t)Mapping - (uintptr_t)CountersBegin + CountersOffset; 185fe6060f1SDimitry Andric 186fe6060f1SDimitry Andric /* Return the memory allocated for counters to OS. */ 187fe6060f1SDimitry Andric lprofReleaseMemoryPagesToOS((uintptr_t)CountersBegin, (uintptr_t)CountersEnd); 1880b57cec5SDimitry Andric } 1890b57cec5SDimitry Andric 1900b57cec5SDimitry Andric #endif 191