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 37*5ffd83dbSDimitry Andric COMPILER_RT_VISIBILITY unsigned lprofProfileDumped() { 38*5ffd83dbSDimitry Andric return 1; 39*5ffd83dbSDimitry Andric } 40*5ffd83dbSDimitry Andric COMPILER_RT_VISIBILITY void lprofSetProfileDumped(unsigned Value) {} 41*5ffd83dbSDimitry Andric 42*5ffd83dbSDimitry Andric COMPILER_RT_VISIBILITY unsigned lprofRuntimeCounterRelocation(void) { 43*5ffd83dbSDimitry Andric return 1; 44*5ffd83dbSDimitry Andric } 45*5ffd83dbSDimitry Andric COMPILER_RT_VISIBILITY void lprofSetRuntimeCounterRelocation(unsigned Value) {} 460b57cec5SDimitry Andric 470b57cec5SDimitry Andric static const char ProfileSinkName[] = "llvm-profile"; 480b57cec5SDimitry Andric 490b57cec5SDimitry Andric static inline void lprofWrite(const char *fmt, ...) { 500b57cec5SDimitry Andric char s[256]; 510b57cec5SDimitry Andric 520b57cec5SDimitry Andric va_list ap; 530b57cec5SDimitry Andric va_start(ap, fmt); 540b57cec5SDimitry Andric int ret = vsnprintf(s, sizeof(s), fmt, ap); 550b57cec5SDimitry Andric va_end(ap); 560b57cec5SDimitry Andric 570b57cec5SDimitry Andric __sanitizer_log_write(s, ret + 1); 580b57cec5SDimitry Andric } 590b57cec5SDimitry Andric 60*5ffd83dbSDimitry Andric struct lprofVMOWriterCtx { 61*5ffd83dbSDimitry Andric /* VMO that contains the profile data for this module. */ 62*5ffd83dbSDimitry Andric zx_handle_t Vmo; 63*5ffd83dbSDimitry Andric /* Current offset within the VMO where data should be written next. */ 64*5ffd83dbSDimitry Andric uint64_t Offset; 65*5ffd83dbSDimitry Andric }; 660b57cec5SDimitry Andric 6768d75effSDimitry Andric static uint32_t lprofVMOWriter(ProfDataWriter *This, ProfDataIOVec *IOVecs, 6868d75effSDimitry Andric uint32_t NumIOVecs) { 69*5ffd83dbSDimitry Andric struct lprofVMOWriterCtx *Ctx = (struct lprofVMOWriterCtx *)This->WriterCtx; 70*5ffd83dbSDimitry Andric 710b57cec5SDimitry Andric /* Compute the total length of data to be written. */ 720b57cec5SDimitry Andric size_t Length = 0; 730b57cec5SDimitry Andric for (uint32_t I = 0; I < NumIOVecs; I++) 740b57cec5SDimitry Andric Length += IOVecs[I].ElmSize * IOVecs[I].NumElm; 750b57cec5SDimitry Andric 760b57cec5SDimitry Andric /* Resize the VMO to ensure there's sufficient space for the data. */ 77*5ffd83dbSDimitry Andric zx_status_t Status = _zx_vmo_set_size(Ctx->Vmo, Ctx->Offset + Length); 780b57cec5SDimitry Andric if (Status != ZX_OK) 790b57cec5SDimitry Andric return -1; 800b57cec5SDimitry Andric 810b57cec5SDimitry Andric /* Copy the data into VMO. */ 820b57cec5SDimitry Andric for (uint32_t I = 0; I < NumIOVecs; I++) { 830b57cec5SDimitry Andric size_t Length = IOVecs[I].ElmSize * IOVecs[I].NumElm; 840b57cec5SDimitry Andric if (IOVecs[I].Data) { 85*5ffd83dbSDimitry Andric Status = _zx_vmo_write(Ctx->Vmo, IOVecs[I].Data, Ctx->Offset, Length); 860b57cec5SDimitry Andric if (Status != ZX_OK) 870b57cec5SDimitry Andric return -1; 88480093f4SDimitry Andric } else if (IOVecs[I].UseZeroPadding) { 89480093f4SDimitry Andric /* Resizing the VMO should zero fill. */ 900b57cec5SDimitry Andric } 91*5ffd83dbSDimitry Andric Ctx->Offset += Length; 920b57cec5SDimitry Andric } 930b57cec5SDimitry Andric 94*5ffd83dbSDimitry Andric /* Record the profile size as a property of the VMO. */ 95*5ffd83dbSDimitry Andric _zx_object_set_property(Ctx->Vmo, ZX_PROP_VMO_CONTENT_SIZE, &Ctx->Offset, 96*5ffd83dbSDimitry Andric sizeof(Ctx->Offset)); 97*5ffd83dbSDimitry Andric 980b57cec5SDimitry Andric return 0; 990b57cec5SDimitry Andric } 1000b57cec5SDimitry Andric 101*5ffd83dbSDimitry Andric static void initVMOWriter(ProfDataWriter *This, struct lprofVMOWriterCtx *Ctx) { 1020b57cec5SDimitry Andric This->Write = lprofVMOWriter; 103*5ffd83dbSDimitry Andric This->WriterCtx = Ctx; 1040b57cec5SDimitry Andric } 1050b57cec5SDimitry Andric 106*5ffd83dbSDimitry Andric /* This method is invoked by the runtime initialization hook 107*5ffd83dbSDimitry Andric * InstrProfilingRuntime.o if it is linked in. */ 108*5ffd83dbSDimitry Andric COMPILER_RT_VISIBILITY 109*5ffd83dbSDimitry Andric void __llvm_profile_initialize(void) { 1100b57cec5SDimitry Andric /* Check if there is llvm/runtime version mismatch. */ 1110b57cec5SDimitry Andric if (GET_VERSION(__llvm_profile_get_version()) != INSTR_PROF_RAW_VERSION) { 11268d75effSDimitry Andric lprofWrite("LLVM Profile: runtime and instrumentation version mismatch: " 1130b57cec5SDimitry Andric "expected %d, but got %d\n", 1140b57cec5SDimitry Andric INSTR_PROF_RAW_VERSION, 1150b57cec5SDimitry Andric (int)GET_VERSION(__llvm_profile_get_version())); 116*5ffd83dbSDimitry Andric return; 1170b57cec5SDimitry Andric } 1180b57cec5SDimitry Andric 119*5ffd83dbSDimitry Andric /* This symbol is defined as weak and initialized to -1 by the runtimer, but 120*5ffd83dbSDimitry Andric * compiler will generate a strong definition initialized to 0 when runtime 121*5ffd83dbSDimitry Andric * counter relocation is used. */ 122*5ffd83dbSDimitry Andric if (__llvm_profile_counter_bias == -1) { 123*5ffd83dbSDimitry Andric lprofWrite("LLVM Profile: counter relocation at runtime is required\n"); 124*5ffd83dbSDimitry Andric return; 125*5ffd83dbSDimitry Andric } 126*5ffd83dbSDimitry Andric 127*5ffd83dbSDimitry Andric const __llvm_profile_data *DataBegin = __llvm_profile_begin_data(); 128*5ffd83dbSDimitry Andric const __llvm_profile_data *DataEnd = __llvm_profile_end_data(); 129*5ffd83dbSDimitry Andric const uint64_t DataSize = __llvm_profile_get_data_size(DataBegin, DataEnd); 130*5ffd83dbSDimitry Andric const uint64_t CountersOffset = 131*5ffd83dbSDimitry Andric sizeof(__llvm_profile_header) + (DataSize * sizeof(__llvm_profile_data)); 132*5ffd83dbSDimitry Andric 133*5ffd83dbSDimitry Andric zx_status_t Status; 134*5ffd83dbSDimitry Andric 135*5ffd83dbSDimitry Andric /* Create VMO to hold the profile data. */ 136*5ffd83dbSDimitry Andric zx_handle_t Vmo = ZX_HANDLE_INVALID; 137*5ffd83dbSDimitry Andric Status = _zx_vmo_create(0, ZX_VMO_RESIZABLE, &Vmo); 138*5ffd83dbSDimitry Andric if (Status != ZX_OK) { 139*5ffd83dbSDimitry Andric lprofWrite("LLVM Profile: cannot create VMO: %s\n", 140*5ffd83dbSDimitry Andric _zx_status_get_string(Status)); 141*5ffd83dbSDimitry Andric return; 142*5ffd83dbSDimitry Andric } 143*5ffd83dbSDimitry Andric 144*5ffd83dbSDimitry Andric /* Give the VMO a name that includes the module signature. */ 145*5ffd83dbSDimitry Andric char VmoName[ZX_MAX_NAME_LEN]; 146*5ffd83dbSDimitry Andric snprintf(VmoName, sizeof(VmoName), "%" PRIu64 ".profraw", 147*5ffd83dbSDimitry Andric lprofGetLoadModuleSignature()); 148*5ffd83dbSDimitry Andric _zx_object_set_property(Vmo, ZX_PROP_NAME, VmoName, strlen(VmoName)); 149*5ffd83dbSDimitry Andric 1500b57cec5SDimitry Andric /* Write the profile data into the mapped region. */ 1510b57cec5SDimitry Andric ProfDataWriter VMOWriter; 152*5ffd83dbSDimitry Andric struct lprofVMOWriterCtx Ctx = {.Vmo = Vmo, .Offset = 0}; 153*5ffd83dbSDimitry Andric initVMOWriter(&VMOWriter, &Ctx); 154*5ffd83dbSDimitry Andric if (lprofWriteData(&VMOWriter, 0, 0) != 0) { 155*5ffd83dbSDimitry Andric lprofWrite("LLVM Profile: failed to write data\n"); 156*5ffd83dbSDimitry Andric _zx_handle_close(Vmo); 157*5ffd83dbSDimitry Andric return; 1580b57cec5SDimitry Andric } 1590b57cec5SDimitry Andric 160*5ffd83dbSDimitry Andric uint64_t Len = 0; 161*5ffd83dbSDimitry Andric Status = _zx_vmo_get_size(Vmo, &Len); 162*5ffd83dbSDimitry Andric if (Status != ZX_OK) { 163*5ffd83dbSDimitry Andric lprofWrite("LLVM Profile: failed to get the VMO size: %s\n", 164*5ffd83dbSDimitry Andric _zx_status_get_string(Status)); 165*5ffd83dbSDimitry Andric _zx_handle_close(Vmo); 166*5ffd83dbSDimitry Andric return; 1670b57cec5SDimitry Andric } 1680b57cec5SDimitry Andric 169*5ffd83dbSDimitry Andric uintptr_t Mapping; 170*5ffd83dbSDimitry Andric Status = 171*5ffd83dbSDimitry Andric _zx_vmar_map(_zx_vmar_root_self(), ZX_VM_PERM_READ | ZX_VM_PERM_WRITE, 0, 172*5ffd83dbSDimitry Andric Vmo, 0, Len, &Mapping); 173*5ffd83dbSDimitry Andric if (Status != ZX_OK) { 174*5ffd83dbSDimitry Andric lprofWrite("LLVM Profile: failed to map the VMO: %s\n", 175*5ffd83dbSDimitry Andric _zx_status_get_string(Status)); 176*5ffd83dbSDimitry Andric _zx_handle_close(Vmo); 177*5ffd83dbSDimitry Andric return; 178*5ffd83dbSDimitry Andric } 1790b57cec5SDimitry Andric 180*5ffd83dbSDimitry Andric /* Publish the VMO which contains profile data to the system. Note that this 181*5ffd83dbSDimitry Andric * also consumes the VMO handle. */ 182*5ffd83dbSDimitry Andric __sanitizer_publish_data(ProfileSinkName, Vmo); 1830b57cec5SDimitry Andric 184*5ffd83dbSDimitry Andric /* Use the dumpfile symbolizer markup element to write the name of VMO. */ 185*5ffd83dbSDimitry Andric lprofWrite("LLVM Profile: {{{dumpfile:%s:%s}}}\n", ProfileSinkName, VmoName); 1860b57cec5SDimitry Andric 187*5ffd83dbSDimitry Andric /* Update the profile fields based on the current mapping. */ 188*5ffd83dbSDimitry Andric __llvm_profile_counter_bias = (intptr_t)Mapping - 189*5ffd83dbSDimitry Andric (uintptr_t)__llvm_profile_begin_counters() + 190*5ffd83dbSDimitry Andric CountersOffset; 1910b57cec5SDimitry Andric } 1920b57cec5SDimitry Andric 1930b57cec5SDimitry Andric #endif 194