xref: /freebsd-src/contrib/llvm-project/compiler-rt/lib/profile/InstrProfilingPlatformFuchsia.c (revision 81ad626541db97eb356e2c1d4a20eb2a26a766ab)
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 
lprofProfileDumped(void)40*81ad6265SDimitry Andric COMPILER_RT_VISIBILITY unsigned lprofProfileDumped(void) {
415ffd83dbSDimitry Andric   return 1;
425ffd83dbSDimitry Andric }
lprofSetProfileDumped(unsigned Value)435ffd83dbSDimitry Andric COMPILER_RT_VISIBILITY void lprofSetProfileDumped(unsigned Value) {}
445ffd83dbSDimitry Andric 
450b57cec5SDimitry Andric static const char ProfileSinkName[] = "llvm-profile";
460b57cec5SDimitry Andric 
lprofWrite(const char * fmt,...)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 
lprofVMOWriter(ProfDataWriter * This,ProfDataIOVec * IOVecs,uint32_t NumIOVecs)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 
initVMOWriter(ProfDataWriter * This,struct lprofVMOWriterCtx * Ctx)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
__llvm_profile_initialize(void)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();
11904eeddc0SDimitry Andric   const char *CountersBegin = __llvm_profile_begin_counters();
12004eeddc0SDimitry 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 =
12304eeddc0SDimitry Andric       sizeof(__llvm_profile_header) + __llvm_write_binary_ids(NULL) + DataSize;
12404eeddc0SDimitry Andric   uint64_t CountersSize =
12504eeddc0SDimitry 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