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 /* VMO that contains the coverage data shared across all modules. */
38 static zx_handle_t __llvm_profile_vmo;
39 /* Current offset within the VMO where data should be written next. */
40 static uint64_t __llvm_profile_offset;
41 
42 static const char ProfileSinkName[] = "llvm-profile";
43 
44 static inline void lprofWrite(const char *fmt, ...) {
45   char s[256];
46 
47   va_list ap;
48   va_start(ap, fmt);
49   int ret = vsnprintf(s, sizeof(s), fmt, ap);
50   va_end(ap);
51 
52   __sanitizer_log_write(s, ret + 1);
53 }
54 
55 static uint32_t lprofVMOWriter(ProfDataWriter *This, ProfDataIOVec *IOVecs,
56                                uint32_t NumIOVecs) {
57   /* Compute the total length of data to be written. */
58   size_t Length = 0;
59   for (uint32_t I = 0; I < NumIOVecs; I++)
60     Length += IOVecs[I].ElmSize * IOVecs[I].NumElm;
61 
62   /* Resize the VMO to ensure there's sufficient space for the data. */
63   zx_status_t Status =
64       _zx_vmo_set_size(__llvm_profile_vmo, __llvm_profile_offset + Length);
65   if (Status != ZX_OK)
66     return -1;
67 
68   /* Copy the data into VMO. */
69   for (uint32_t I = 0; I < NumIOVecs; I++) {
70     size_t Length = IOVecs[I].ElmSize * IOVecs[I].NumElm;
71     if (IOVecs[I].Data) {
72       Status = _zx_vmo_write(__llvm_profile_vmo, IOVecs[I].Data,
73                              __llvm_profile_offset, Length);
74       if (Status != ZX_OK)
75         return -1;
76     } else if (IOVecs[I].UseZeroPadding) {
77       /* Resizing the VMO should zero fill. */
78     }
79     __llvm_profile_offset += Length;
80   }
81 
82   return 0;
83 }
84 
85 static void initVMOWriter(ProfDataWriter *This) {
86   This->Write = lprofVMOWriter;
87   This->WriterCtx = NULL;
88 }
89 
90 static int dump(void) {
91   if (lprofProfileDumped()) {
92     lprofWrite("LLVM Profile: data not published: already written.\n");
93     return 0;
94   }
95 
96   /* Check if there is llvm/runtime version mismatch. */
97   if (GET_VERSION(__llvm_profile_get_version()) != INSTR_PROF_RAW_VERSION) {
98     lprofWrite("LLVM Profile: runtime and instrumentation version mismatch: "
99                "expected %d, but got %d\n",
100                INSTR_PROF_RAW_VERSION,
101                (int)GET_VERSION(__llvm_profile_get_version()));
102     return -1;
103   }
104 
105   /* Write the profile data into the mapped region. */
106   ProfDataWriter VMOWriter;
107   initVMOWriter(&VMOWriter);
108   if (lprofWriteData(&VMOWriter, lprofGetVPDataReader(), 0) != 0)
109     return -1;
110 
111   return 0;
112 }
113 
114 COMPILER_RT_VISIBILITY
115 int __llvm_profile_dump(void) {
116   int rc = dump();
117   lprofSetProfileDumped();
118   return rc;
119 }
120 
121 static void dumpWithoutReturn(void) { dump(); }
122 
123 static void createVMO(void) {
124   /* Don't create VMO if it has been alread created. */
125   if (__llvm_profile_vmo != ZX_HANDLE_INVALID)
126     return;
127 
128   const __llvm_profile_data *DataBegin = __llvm_profile_begin_data();
129   const __llvm_profile_data *DataEnd = __llvm_profile_end_data();
130   const uint64_t DataSize = __llvm_profile_get_data_size(DataBegin, DataEnd);
131   const uint64_t CountersOffset = sizeof(__llvm_profile_header) +
132     (DataSize * sizeof(__llvm_profile_data));
133 
134   zx_status_t Status;
135 
136   /* Create VMO to hold the profile data. */
137   Status = _zx_vmo_create(0, ZX_VMO_RESIZABLE, &__llvm_profile_vmo);
138   if (Status != ZX_OK) {
139     lprofWrite("LLVM Profile: cannot create VMO: %s\n",
140                _zx_status_get_string(Status));
141     return;
142   }
143 
144   /* Give the VMO a name that includes the module signature. */
145   char VmoName[ZX_MAX_NAME_LEN];
146   snprintf(VmoName, sizeof(VmoName), "%" PRIu64 ".profraw",
147            lprofGetLoadModuleSignature());
148   _zx_object_set_property(__llvm_profile_vmo, ZX_PROP_NAME, VmoName,
149                           strlen(VmoName));
150 
151   /* Duplicate the handle since __sanitizer_publish_data consumes it. */
152   zx_handle_t Handle;
153   Status =
154       _zx_handle_duplicate(__llvm_profile_vmo, ZX_RIGHT_SAME_RIGHTS, &Handle);
155   if (Status != ZX_OK) {
156     lprofWrite("LLVM Profile: cannot duplicate VMO handle: %s\n",
157                _zx_status_get_string(Status));
158     _zx_handle_close(__llvm_profile_vmo);
159     __llvm_profile_vmo = ZX_HANDLE_INVALID;
160     return;
161   }
162 
163   /* Publish the VMO which contains profile data to the system. */
164   __sanitizer_publish_data(ProfileSinkName, Handle);
165 
166   /* Use the dumpfile symbolizer markup element to write the name of VMO. */
167   lprofWrite("LLVM Profile: {{{dumpfile:%s:%s}}}\n", ProfileSinkName, VmoName);
168 
169   /* Check if there is llvm/runtime version mismatch. */
170   if (GET_VERSION(__llvm_profile_get_version()) != INSTR_PROF_RAW_VERSION) {
171     lprofWrite("LLVM Profile: runtime and instrumentation version mismatch: "
172                "expected %d, but got %d\n",
173                INSTR_PROF_RAW_VERSION,
174                (int)GET_VERSION(__llvm_profile_get_version()));
175     return;
176   }
177 
178   /* Write the profile data into the mapped region. */
179   ProfDataWriter VMOWriter;
180   initVMOWriter(&VMOWriter);
181   if (lprofWriteData(&VMOWriter, 0, 0) != 0) {
182     lprofWrite("LLVM Profile: failed to write data\n");
183     return;
184   }
185 
186   uint64_t Len = 0;
187   Status = _zx_vmo_get_size(__llvm_profile_vmo, &Len);
188   if (Status != ZX_OK) {
189     lprofWrite("LLVM Profile: failed to get the VMO size: %s\n",
190                _zx_status_get_string(Status));
191     return;
192   }
193 
194   uintptr_t Mapping;
195   Status =
196       _zx_vmar_map(_zx_vmar_root_self(), ZX_VM_PERM_READ | ZX_VM_PERM_WRITE,
197                    0, __llvm_profile_vmo, 0, Len, &Mapping);
198   if (Status != ZX_OK) {
199     lprofWrite("LLVM Profile: failed to map the VMO: %s\n",
200                _zx_status_get_string(Status));
201     return;
202   }
203 
204   /* Update the profile fields based on the current mapping. */
205   __llvm_profile_counter_bias = (intptr_t)Mapping -
206       (uintptr_t)__llvm_profile_begin_counters() + CountersOffset;
207 }
208 
209 /* This method is invoked by the runtime initialization hook
210  * InstrProfilingRuntime.o if it is linked in.
211  */
212 COMPILER_RT_VISIBILITY
213 void __llvm_profile_initialize_file(void) { createVMO(); }
214 
215 COMPILER_RT_VISIBILITY
216 int __llvm_profile_register_write_file_atexit(void) {
217   static bool HasBeenRegistered = false;
218 
219   if (HasBeenRegistered)
220     return 0;
221 
222   lprofSetupValueProfiler();
223 
224   HasBeenRegistered = true;
225   return atexit(dumpWithoutReturn);
226 }
227 
228 #endif
229