1 // SPDX-License-Identifier: (BSD-2-Clause AND BSD-3-Clause) 2 /* 3 * Copyright (c) 2016, Linaro Limited 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions are met: 8 * 9 * 1. Redistributions of source code must retain the above copyright notice, 10 * this list of conditions and the following disclaimer. 11 * 12 * 2. Redistributions in binary form must reproduce the above copyright notice, 13 * this list of conditions and the following disclaimer in the documentation 14 * and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 /* 30 * Portions of this file are adapted from glibc: 31 * gmon/gmon.c 32 * gmon/mcount.c 33 * 34 *- 35 * Copyright (c) 1983, 1992, 1993, 2011 36 * The Regents of the University of California. All rights reserved. 37 * 38 * Redistribution and use in source and binary forms, with or without 39 * modification, are permitted provided that the following conditions 40 * are met: 41 * 1. Redistributions of source code must retain the above copyright 42 * notice, this list of conditions and the following disclaimer. 43 * 2. Redistributions in binary form must reproduce the above copyright 44 * notice, this list of conditions and the following disclaimer in the 45 * documentation and/or other materials provided with the distribution. 46 * 4. Neither the name of the University nor the names of its contributors 47 * may be used to endorse or promote products derived from this software 48 * without specific prior written permission. 49 * 50 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 51 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 52 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 53 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 54 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 55 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 56 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 57 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 58 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 59 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 60 * SUCH DAMAGE. 61 */ 62 63 #include <assert.h> 64 #include <compiler.h> 65 #include <inttypes.h> 66 #include <malloc.h> 67 #include <stdint.h> 68 #include <string.h> 69 #include <tee_api_private.h> 70 #include <trace.h> 71 #include <user_ta_header.h> 72 #include <utee_types.h> 73 #include "gmon.h" 74 #include "gmon_out.h" 75 #include "gprof_pta.h" 76 77 /* Defined by the linker script */ 78 extern uint8_t __gprof_buf_end[]; 79 extern uint8_t __gprof_buf_start[]; 80 81 static bool ta_instrumented(void) 82 { 83 return (__gprof_buf_end != __gprof_buf_start); 84 } 85 86 static void *gprof_alloc(size_t len) 87 { 88 if (len > (size_t)(__gprof_buf_end - __gprof_buf_start)) 89 return NULL; 90 return __gprof_buf_start; 91 } 92 93 static struct gmonparam _gmonparam = { GMON_PROF_OFF }; 94 95 static uint32_t _gprof_file_id; /* File id returned by tee-supplicant */ 96 97 static int _gprof_s_scale; 98 #define SCALE_1_TO_1 0x10000L 99 100 /* Adjust PC so that gprof can locate it in the TA ELF file */ 101 static unsigned long __noprof adjust_pc(unsigned long pc) 102 { 103 return pc - (unsigned long)__text_start + sizeof(struct ta_head); 104 } 105 106 void __utee_gprof_init(void) 107 { 108 unsigned long lowpc; 109 unsigned long highpc; 110 struct gmonparam *p = &_gmonparam; 111 size_t bufsize; 112 TEE_Result res; 113 char *cp; 114 115 if (!ta_instrumented()) 116 return; 117 118 lowpc = adjust_pc((unsigned long)__text_start); 119 highpc = adjust_pc((unsigned long)__text_end); 120 121 /* 122 * Round lowpc and highpc to multiples of the density we're using 123 * so the rest of the scaling (here and in gprof) stays in ints. 124 */ 125 p->lowpc = ROUNDDOWN(lowpc, HISTFRACTION * sizeof(HISTCOUNTER)); 126 p->highpc = ROUNDUP(highpc, HISTFRACTION * sizeof(HISTCOUNTER)); 127 p->textsize = p->highpc - p->lowpc; 128 p->kcountsize = ROUNDUP(p->textsize / HISTFRACTION, sizeof(*p->froms)); 129 p->hashfraction = HASHFRACTION; 130 p->log_hashfraction = -1; 131 /* 132 * The following test must be kept in sync with the corresponding 133 * test in __mcount_internal 134 */ 135 if ((HASHFRACTION & (HASHFRACTION - 1)) == 0) { 136 /* 137 * If HASHFRACTION is a power of two, mcount can use shifting 138 * instead of integer division. Precompute shift amount. 139 */ 140 p->log_hashfraction = __builtin_ffs(p->hashfraction * 141 sizeof(*p->froms)) - 1; 142 } 143 p->fromssize = p->textsize / HASHFRACTION; 144 p->tolimit = p->textsize * ARCDENSITY / 100; 145 if (p->tolimit < MINARCS) 146 p->tolimit = MINARCS; 147 else if (p->tolimit > MAXARCS) 148 p->tolimit = MAXARCS; 149 p->tossize = p->tolimit * sizeof(struct tostruct); 150 151 bufsize = p->kcountsize + p->fromssize + p->tossize; 152 153 IMSG("gprof: initializing"); 154 DMSG("TA text size: %zu, gprof buffer size: %zu", 155 __text_end - __text_start, bufsize); 156 157 cp = gprof_alloc(bufsize); 158 if (!cp) { 159 EMSG("gprof: could not allocate profiling buffer"); 160 p->tos = NULL; 161 p->state = GMON_PROF_ERROR; 162 return; 163 } 164 165 p->tos = (struct tostruct *)cp; 166 cp += p->tossize; 167 p->kcount = (HISTCOUNTER *)cp; 168 cp += p->kcountsize; 169 p->froms = (ARCINDEX *)cp; 170 171 p->tos[0].link = 0; 172 173 if (p->kcountsize < p->textsize) 174 _gprof_s_scale = ((float)p->kcountsize / p->textsize) * 175 SCALE_1_TO_1; 176 else 177 _gprof_s_scale = SCALE_1_TO_1; 178 179 res = __pta_gprof_pc_sampling_start(p->kcount, p->kcountsize, 180 p->lowpc + 181 ((unsigned long)__text_start - 182 sizeof(struct ta_head)), 183 _gprof_s_scale); 184 if (res != TEE_SUCCESS) 185 EMSG("gprof: could not start PC sampling (0x%08x)", res); 186 187 p->state = GMON_PROF_ON; 188 } 189 190 static void _gprof_write_buf(void *buf, size_t size) 191 { 192 TEE_Result res; 193 194 res = __pta_gprof_send(buf, size, &_gprof_file_id); 195 if (res != TEE_SUCCESS) 196 EMSG("gprof: could not send gprof data (0x%08x)", res); 197 } 198 199 static void _gprof_write_header(void) 200 { 201 struct gmon_hdr ghdr; 202 size_t size = sizeof(struct gmon_hdr); 203 204 memcpy(&ghdr.cookie[0], GMON_MAGIC, sizeof(ghdr.cookie)); 205 ghdr.version = GMON_VERSION; 206 memset(ghdr.spare, '\0', sizeof(ghdr.spare)); 207 208 _gprof_write_buf(&ghdr, size); 209 } 210 211 static void _gprof_write_hist(void) 212 { 213 struct out_record { 214 uint8_t tag; 215 struct gmon_hist_hdr hist_hdr; 216 } __packed out = { 217 .tag = GMON_TAG_TIME_HIST, 218 .hist_hdr = { 219 .low_pc = _gmonparam.lowpc, 220 .high_pc = _gmonparam.highpc, 221 .hist_size = _gmonparam.kcountsize/sizeof(HISTCOUNTER), 222 .prof_rate = _gmonparam.prof_rate, 223 .dimen = "seconds", 224 .dimen_abbrev = 's', 225 } 226 }; 227 228 _gprof_write_buf(&out, sizeof(out)); 229 _gprof_write_buf(_gmonparam.kcount, _gmonparam.kcountsize); 230 } 231 232 static void _gprof_write_call_graph(void) 233 { 234 #define NARCS_PER_WRITE 16 235 struct out_record { 236 uint8_t tag; 237 uint8_t data[sizeof(struct gmon_cg_arc_record)]; 238 } out[NARCS_PER_WRITE]; 239 struct gmon_cg_arc_record arc; 240 ARCINDEX from_index, to_index; 241 unsigned long from_len; 242 unsigned long frompc; 243 int nfilled = 0; 244 245 from_len = _gmonparam.fromssize / sizeof(*_gmonparam.froms); 246 247 for (from_index = 0; from_index < from_len; ++from_index) { 248 249 if (_gmonparam.froms[from_index] == 0) 250 continue; 251 252 frompc = _gmonparam.lowpc; 253 frompc += (from_index * _gmonparam.hashfraction 254 * sizeof(*_gmonparam.froms)); 255 for (to_index = _gmonparam.froms[from_index]; 256 to_index != 0; 257 to_index = _gmonparam.tos[to_index].link) { 258 259 arc.from_pc = frompc; 260 arc.self_pc = _gmonparam.tos[to_index].selfpc; 261 arc.count = _gmonparam.tos[to_index].count; 262 263 out[nfilled].tag = GMON_TAG_CG_ARC; 264 memcpy(out[nfilled].data, &arc, sizeof(arc)); 265 266 if (++nfilled == NARCS_PER_WRITE) { 267 _gprof_write_buf(out, sizeof(out)); 268 nfilled = 0; 269 } 270 } 271 } 272 if (nfilled > 0) 273 _gprof_write_buf(out, nfilled * sizeof(out[0])); 274 } 275 276 /* Stop profiling and send profile data in gmon.out format to Normal World */ 277 void __utee_gprof_fini(void) 278 { 279 TEE_Result res; 280 281 if (_gmonparam.state != GMON_PROF_ON) 282 return; 283 284 /* Stop call graph tracing */ 285 _gmonparam.state = GMON_PROF_OFF_EXITING; 286 287 /* Stop TA sampling */ 288 res = __pta_gprof_pc_sampling_stop(&_gmonparam.prof_rate); 289 290 _gprof_write_header(); 291 if (res == TEE_SUCCESS) 292 _gprof_write_hist(); 293 _gprof_write_call_graph(); 294 295 __pta_gprof_fini(); 296 } 297 298 /* 299 * Called from the assembly stub (_mcount or __gnu_mcount_nc). 300 * 301 * __mcount_internal updates data structures that represent traversals of the 302 * program's call graph edges. frompc and selfpc are the return 303 * address and function address that represents the given call graph edge. 304 */ 305 void __noprof __mcount_internal(unsigned long frompc, unsigned long selfpc) 306 { 307 ARCINDEX *frompcindex; 308 struct tostruct *top, *prevtop; 309 struct gmonparam *p; 310 ARCINDEX toindex; 311 int i; 312 313 p = &_gmonparam; 314 315 /* 316 * Check that we are profiling and that we aren't recursively invoked. 317 */ 318 if (p->state != GMON_PROF_ON) 319 return; 320 p->state = GMON_PROF_BUSY; 321 322 frompc = adjust_pc(frompc); 323 selfpc = adjust_pc(selfpc); 324 325 /* Check that frompcindex is a reasonable pc value. */ 326 frompc -= p->lowpc; 327 if (frompc > p->textsize) 328 goto done; 329 330 /* Note: keep in sync. with the initialization function above */ 331 if ((HASHFRACTION & (HASHFRACTION - 1)) == 0) { 332 /* Avoid integer divide if possible */ 333 i = frompc >> p->log_hashfraction; 334 } else { 335 i = frompc / (p->hashfraction * sizeof(*p->froms)); 336 } 337 frompcindex = &p->froms[i]; 338 toindex = *frompcindex; 339 if (toindex == 0) { 340 /* First time traversing this arc */ 341 toindex = ++p->tos[0].link; 342 if (toindex >= p->tolimit) { 343 /* Halt further profiling */ 344 goto overflow; 345 } 346 347 *frompcindex = toindex; 348 top = &p->tos[toindex]; 349 top->selfpc = selfpc; 350 top->count = 1; 351 top->link = 0; 352 goto done; 353 } 354 top = &p->tos[toindex]; 355 if (top->selfpc == selfpc) { 356 /* Arc at front of chain; usual case */ 357 top->count++; 358 goto done; 359 } 360 /* 361 * Have to go looking down chain for it. 362 * top points to what we are looking at, 363 * prevtop points to previous top. 364 * we know it is not at the head of the chain. 365 */ 366 for (;;) { 367 if (top->link == 0) { 368 /* 369 * top is end of the chain and none of the chain 370 * had top->selfpc == selfpc. 371 * so we allocate a new tostruct 372 * and link it to the head of the chain. 373 */ 374 toindex = ++p->tos[0].link; 375 if (toindex >= p->tolimit) 376 goto overflow; 377 378 top = &p->tos[toindex]; 379 top->selfpc = selfpc; 380 top->count = 1; 381 top->link = *frompcindex; 382 *frompcindex = toindex; 383 goto done; 384 } 385 /* 386 * Otherwise, check the next arc on the chain. 387 */ 388 prevtop = top; 389 top = &p->tos[top->link]; 390 if (top->selfpc == selfpc) { 391 /* 392 * There it is. Increment its count, move it to the 393 * head of the chain. 394 */ 395 top->count++; 396 toindex = prevtop->link; 397 prevtop->link = top->link; 398 top->link = *frompcindex; 399 *frompcindex = toindex; 400 goto done; 401 } 402 } 403 done: 404 p->state = GMON_PROF_ON; 405 return; 406 overflow: 407 p->state = GMON_PROF_ERROR; 408 } 409