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