1 | /*===- InstrProfilingPlatformFuchsia.c - Profile data Fuchsia platform ----===*\ |
2 | |* |
3 | |* Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | |* See https://llvm.org/LICENSE.txt for license information. |
5 | |* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | |* |
7 | \*===----------------------------------------------------------------------===*/ |
8 | /* |
9 | * This file implements the profiling runtime for Fuchsia and defines the |
10 | * shared profile runtime interface. Each module (executable or DSO) statically |
11 | * links in the whole profile runtime to satisfy the calls from its |
12 | * instrumented code. Several modules in the same program might be separately |
13 | * compiled and even use different versions of the instrumentation ABI and data |
14 | * format. All they share in common is the VMO and the offset, which live in |
15 | * exported globals so that exactly one definition will be shared across all |
16 | * modules. Each module has its own independent runtime that registers its own |
17 | * atexit hook to append its own data into the shared VMO which is published |
18 | * via the data sink hook provided by Fuchsia's dynamic linker. |
19 | */ |
20 | |
21 | #if defined(__Fuchsia__) |
22 | |
23 | #include <inttypes.h> |
24 | #include <stdarg.h> |
25 | #include <stdbool.h> |
26 | #include <stdlib.h> |
27 | |
28 | #include <zircon/process.h> |
29 | #include <zircon/sanitizer.h> |
30 | #include <zircon/status.h> |
31 | #include <zircon/syscalls.h> |
32 | |
33 | #include "InstrProfiling.h" |
34 | #include "InstrProfilingInternal.h" |
35 | #include "InstrProfilingUtil.h" |
36 | |
37 | /* This variable is an external reference to symbol defined by the compiler. */ |
38 | COMPILER_RT_VISIBILITY extern intptr_t INSTR_PROF_PROFILE_COUNTER_BIAS_VAR; |
39 | |
40 | COMPILER_RT_VISIBILITY unsigned lprofProfileDumped(void) { |
41 | return 1; |
42 | } |
43 | COMPILER_RT_VISIBILITY void lprofSetProfileDumped(unsigned Value) {} |
44 | |
45 | static const char ProfileSinkName[] = "llvm-profile" ; |
46 | |
47 | static inline void lprofWrite(const char *fmt, ...) { |
48 | char s[256]; |
49 | |
50 | va_list ap; |
51 | va_start(ap, fmt); |
52 | int ret = vsnprintf(s, sizeof(s), fmt, ap); |
53 | va_end(ap); |
54 | |
55 | __sanitizer_log_write(s, ret); |
56 | } |
57 | |
58 | struct lprofVMOWriterCtx { |
59 | /* VMO that contains the profile data for this module. */ |
60 | zx_handle_t Vmo; |
61 | /* Current offset within the VMO where data should be written next. */ |
62 | uint64_t Offset; |
63 | }; |
64 | |
65 | static uint32_t lprofVMOWriter(ProfDataWriter *This, ProfDataIOVec *IOVecs, |
66 | uint32_t NumIOVecs) { |
67 | struct lprofVMOWriterCtx *Ctx = (struct lprofVMOWriterCtx *)This->WriterCtx; |
68 | |
69 | /* Compute the total length of data to be written. */ |
70 | size_t Length = 0; |
71 | for (uint32_t I = 0; I < NumIOVecs; I++) |
72 | Length += IOVecs[I].ElmSize * IOVecs[I].NumElm; |
73 | |
74 | /* Resize the VMO to ensure there's sufficient space for the data. */ |
75 | zx_status_t Status = _zx_vmo_set_size(Ctx->Vmo, Ctx->Offset + Length); |
76 | if (Status != ZX_OK) |
77 | return -1; |
78 | |
79 | /* Copy the data into VMO. */ |
80 | for (uint32_t I = 0; I < NumIOVecs; I++) { |
81 | size_t Length = IOVecs[I].ElmSize * IOVecs[I].NumElm; |
82 | if (IOVecs[I].Data) { |
83 | Status = _zx_vmo_write(Ctx->Vmo, IOVecs[I].Data, Ctx->Offset, Length); |
84 | if (Status != ZX_OK) |
85 | return -1; |
86 | } else if (IOVecs[I].UseZeroPadding) { |
87 | /* Resizing the VMO should zero fill. */ |
88 | } |
89 | Ctx->Offset += Length; |
90 | } |
91 | |
92 | /* Record the profile size as a property of the VMO. */ |
93 | _zx_object_set_property(Ctx->Vmo, ZX_PROP_VMO_CONTENT_SIZE, &Ctx->Offset, |
94 | sizeof(Ctx->Offset)); |
95 | |
96 | return 0; |
97 | } |
98 | |
99 | static void initVMOWriter(ProfDataWriter *This, struct lprofVMOWriterCtx *Ctx) { |
100 | This->Write = lprofVMOWriter; |
101 | This->WriterCtx = Ctx; |
102 | } |
103 | |
104 | /* This method is invoked by the runtime initialization hook |
105 | * InstrProfilingRuntime.o if it is linked in. */ |
106 | COMPILER_RT_VISIBILITY |
107 | void __llvm_profile_initialize(void) { |
108 | /* Check if there is llvm/runtime version mismatch. */ |
109 | if (GET_VERSION(__llvm_profile_get_version()) != INSTR_PROF_RAW_VERSION) { |
110 | lprofWrite("LLVM Profile: runtime and instrumentation version mismatch: " |
111 | "expected %d, but got %d\n" , |
112 | INSTR_PROF_RAW_VERSION, |
113 | (int)GET_VERSION(__llvm_profile_get_version())); |
114 | return; |
115 | } |
116 | |
117 | const __llvm_profile_data *DataBegin = __llvm_profile_begin_data(); |
118 | const __llvm_profile_data *DataEnd = __llvm_profile_end_data(); |
119 | const char *CountersBegin = __llvm_profile_begin_counters(); |
120 | const char *CountersEnd = __llvm_profile_end_counters(); |
121 | const uint64_t DataSize = __llvm_profile_get_data_size(DataBegin, DataEnd); |
122 | const uint64_t CountersOffset = |
123 | sizeof(__llvm_profile_header) + __llvm_write_binary_ids(NULL) + DataSize; |
124 | uint64_t CountersSize = |
125 | __llvm_profile_get_counters_size(CountersBegin, CountersEnd); |
126 | |
127 | /* Don't publish a VMO if there are no counters. */ |
128 | if (!CountersSize) |
129 | return; |
130 | |
131 | zx_status_t Status; |
132 | |
133 | /* Create a VMO to hold the profile data. */ |
134 | zx_handle_t Vmo = ZX_HANDLE_INVALID; |
135 | Status = _zx_vmo_create(0, ZX_VMO_RESIZABLE, &Vmo); |
136 | if (Status != ZX_OK) { |
137 | lprofWrite("LLVM Profile: cannot create VMO: %s\n" , |
138 | _zx_status_get_string(Status)); |
139 | return; |
140 | } |
141 | |
142 | /* Give the VMO a name that includes the module signature. */ |
143 | char VmoName[ZX_MAX_NAME_LEN]; |
144 | snprintf(VmoName, sizeof(VmoName), "%" PRIu64 ".profraw" , |
145 | lprofGetLoadModuleSignature()); |
146 | _zx_object_set_property(Vmo, ZX_PROP_NAME, VmoName, strlen(VmoName)); |
147 | |
148 | /* Write the profile data into the mapped region. */ |
149 | ProfDataWriter VMOWriter; |
150 | struct lprofVMOWriterCtx Ctx = {.Vmo = Vmo, .Offset = 0}; |
151 | initVMOWriter(&VMOWriter, &Ctx); |
152 | if (lprofWriteData(&VMOWriter, 0, 0) != 0) { |
153 | lprofWrite("LLVM Profile: failed to write data\n" ); |
154 | _zx_handle_close(Vmo); |
155 | return; |
156 | } |
157 | |
158 | uint64_t Len = 0; |
159 | Status = _zx_vmo_get_size(Vmo, &Len); |
160 | if (Status != ZX_OK) { |
161 | lprofWrite("LLVM Profile: failed to get the VMO size: %s\n" , |
162 | _zx_status_get_string(Status)); |
163 | _zx_handle_close(Vmo); |
164 | return; |
165 | } |
166 | |
167 | uintptr_t Mapping; |
168 | Status = |
169 | _zx_vmar_map(_zx_vmar_root_self(), ZX_VM_PERM_READ | ZX_VM_PERM_WRITE, 0, |
170 | Vmo, 0, Len, &Mapping); |
171 | if (Status != ZX_OK) { |
172 | lprofWrite("LLVM Profile: failed to map the VMO: %s\n" , |
173 | _zx_status_get_string(Status)); |
174 | _zx_handle_close(Vmo); |
175 | return; |
176 | } |
177 | |
178 | /* Publish the VMO which contains profile data to the system. Note that this |
179 | * also consumes the VMO handle. */ |
180 | __sanitizer_publish_data(ProfileSinkName, Vmo); |
181 | |
182 | /* Update the profile fields based on the current mapping. */ |
183 | INSTR_PROF_PROFILE_COUNTER_BIAS_VAR = |
184 | (intptr_t)Mapping - (uintptr_t)CountersBegin + CountersOffset; |
185 | |
186 | /* Return the memory allocated for counters to OS. */ |
187 | lprofReleaseMemoryPagesToOS((uintptr_t)CountersBegin, (uintptr_t)CountersEnd); |
188 | } |
189 | |
190 | #endif |
191 | |