uboot/lib/trace.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Copyright (c) 2012 The Chromium OS Authors.
   4 */
   5
   6#include <common.h>
   7#include <mapmem.h>
   8#include <time.h>
   9#include <trace.h>
  10#include <asm/io.h>
  11#include <asm/sections.h>
  12
  13DECLARE_GLOBAL_DATA_PTR;
  14
  15static char trace_enabled __attribute__((section(".data")));
  16static char trace_inited __attribute__((section(".data")));
  17
  18/* The header block at the start of the trace memory area */
  19struct trace_hdr {
  20        int func_count;         /* Total number of function call sites */
  21        u64 call_count;         /* Total number of tracked function calls */
  22        u64 untracked_count;    /* Total number of untracked function calls */
  23        int funcs_used;         /* Total number of functions used */
  24
  25        /*
  26         * Call count for each function. This is indexed by the word offset
  27         * of the function from gd->relocaddr
  28         */
  29        uintptr_t *call_accum;
  30
  31        /* Function trace list */
  32        struct trace_call *ftrace;      /* The function call records */
  33        ulong ftrace_size;      /* Num. of ftrace records we have space for */
  34        ulong ftrace_count;     /* Num. of ftrace records written */
  35        ulong ftrace_too_deep_count;    /* Functions that were too deep */
  36
  37        int depth;
  38        int depth_limit;
  39        int max_depth;
  40};
  41
  42static struct trace_hdr *hdr;   /* Pointer to start of trace buffer */
  43
  44static inline uintptr_t __attribute__((no_instrument_function))
  45                func_ptr_to_num(void *func_ptr)
  46{
  47        uintptr_t offset = (uintptr_t)func_ptr;
  48
  49#ifdef CONFIG_SANDBOX
  50        offset -= (uintptr_t)&_init;
  51#else
  52        if (gd->flags & GD_FLG_RELOC)
  53                offset -= gd->relocaddr;
  54        else
  55                offset -= CONFIG_SYS_TEXT_BASE;
  56#endif
  57        return offset / FUNC_SITE_SIZE;
  58}
  59
  60#ifdef CONFIG_EFI_LOADER
  61
  62/**
  63 * trace_gd - the value of the gd register
  64 */
  65static volatile void *trace_gd;
  66
  67/**
  68 * trace_save_gd() - save the value of the gd register
  69 */
  70static void __attribute__((no_instrument_function)) trace_save_gd(void)
  71{
  72        trace_gd = gd;
  73}
  74
  75/**
  76 * trace_swap_gd() - swap between U-Boot and application gd register value
  77 *
  78 * An UEFI application may change the value of the register that gd lives in.
  79 * But some of our functions like get_ticks() access this register. So we
  80 * have to set the gd register to the U-Boot value when entering a trace
  81 * point and set it back to the application value when exiting the trace point.
  82 */
  83static void __attribute__((no_instrument_function)) trace_swap_gd(void)
  84{
  85        volatile void *temp_gd = trace_gd;
  86
  87        trace_gd = gd;
  88        gd = temp_gd;
  89}
  90
  91#else
  92
  93static void __attribute__((no_instrument_function)) trace_save_gd(void)
  94{
  95}
  96
  97static void __attribute__((no_instrument_function)) trace_swap_gd(void)
  98{
  99}
 100
 101#endif
 102
 103static void __attribute__((no_instrument_function)) add_ftrace(void *func_ptr,
 104                                void *caller, ulong flags)
 105{
 106        if (hdr->depth > hdr->depth_limit) {
 107                hdr->ftrace_too_deep_count++;
 108                return;
 109        }
 110        if (hdr->ftrace_count < hdr->ftrace_size) {
 111                struct trace_call *rec = &hdr->ftrace[hdr->ftrace_count];
 112
 113                rec->func = func_ptr_to_num(func_ptr);
 114                rec->caller = func_ptr_to_num(caller);
 115                rec->flags = flags | (timer_get_us() & FUNCF_TIMESTAMP_MASK);
 116        }
 117        hdr->ftrace_count++;
 118}
 119
 120static void __attribute__((no_instrument_function)) add_textbase(void)
 121{
 122        if (hdr->ftrace_count < hdr->ftrace_size) {
 123                struct trace_call *rec = &hdr->ftrace[hdr->ftrace_count];
 124
 125                rec->func = CONFIG_SYS_TEXT_BASE;
 126                rec->caller = 0;
 127                rec->flags = FUNCF_TEXTBASE;
 128        }
 129        hdr->ftrace_count++;
 130}
 131
 132/**
 133 * __cyg_profile_func_enter() - record function entry
 134 *
 135 * We add to our tally for this function and add to the list of called
 136 * functions.
 137 *
 138 * @func_ptr:   pointer to function being entered
 139 * @caller:     pointer to function which called this function
 140 */
 141void __attribute__((no_instrument_function)) __cyg_profile_func_enter(
 142                void *func_ptr, void *caller)
 143{
 144        if (trace_enabled) {
 145                int func;
 146
 147                trace_swap_gd();
 148                add_ftrace(func_ptr, caller, FUNCF_ENTRY);
 149                func = func_ptr_to_num(func_ptr);
 150                if (func < hdr->func_count) {
 151                        hdr->call_accum[func]++;
 152                        hdr->call_count++;
 153                } else {
 154                        hdr->untracked_count++;
 155                }
 156                hdr->depth++;
 157                if (hdr->depth > hdr->depth_limit)
 158                        hdr->max_depth = hdr->depth;
 159                trace_swap_gd();
 160        }
 161}
 162
 163/**
 164 * __cyg_profile_func_exit() - record function exit
 165 *
 166 * @func_ptr:   pointer to function being entered
 167 * @caller:     pointer to function which called this function
 168 */
 169void __attribute__((no_instrument_function)) __cyg_profile_func_exit(
 170                void *func_ptr, void *caller)
 171{
 172        if (trace_enabled) {
 173                trace_swap_gd();
 174                add_ftrace(func_ptr, caller, FUNCF_EXIT);
 175                hdr->depth--;
 176                trace_swap_gd();
 177        }
 178}
 179
 180/**
 181 * trace_list_functions() - produce a list of called functions
 182 *
 183 * The information is written into the supplied buffer - a header followed
 184 * by a list of function records.
 185 *
 186 * @buff:       buffer to place list into
 187 * @buff_size:  size of buffer
 188 * @needed:     returns size of buffer needed, which may be
 189 *              greater than buff_size if we ran out of space.
 190 * Return:      0 if ok, -ENOSPC if space was exhausted
 191 */
 192int trace_list_functions(void *buff, size_t buff_size, size_t *needed)
 193{
 194        struct trace_output_hdr *output_hdr = NULL;
 195        void *end, *ptr = buff;
 196        size_t func;
 197        size_t upto;
 198
 199        end = buff ? buff + buff_size : NULL;
 200
 201        /* Place some header information */
 202        if (ptr + sizeof(struct trace_output_hdr) < end)
 203                output_hdr = ptr;
 204        ptr += sizeof(struct trace_output_hdr);
 205
 206        /* Add information about each function */
 207        for (func = upto = 0; func < hdr->func_count; func++) {
 208                size_t calls = hdr->call_accum[func];
 209
 210                if (!calls)
 211                        continue;
 212
 213                if (ptr + sizeof(struct trace_output_func) < end) {
 214                        struct trace_output_func *stats = ptr;
 215
 216                        stats->offset = func * FUNC_SITE_SIZE;
 217                        stats->call_count = calls;
 218                        upto++;
 219                }
 220                ptr += sizeof(struct trace_output_func);
 221        }
 222
 223        /* Update the header */
 224        if (output_hdr) {
 225                output_hdr->rec_count = upto;
 226                output_hdr->type = TRACE_CHUNK_FUNCS;
 227        }
 228
 229        /* Work out how must of the buffer we used */
 230        *needed = ptr - buff;
 231        if (ptr > end)
 232                return -ENOSPC;
 233
 234        return 0;
 235}
 236
 237/**
 238 * trace_list_functions() - produce a list of function calls
 239 *
 240 * The information is written into the supplied buffer - a header followed
 241 * by a list of function records.
 242 *
 243 * @buff:       buffer to place list into
 244 * @buff_size:  size of buffer
 245 * @needed:     returns size of buffer needed, which may be
 246 *              greater than buff_size if we ran out of space.
 247 * Return:      0 if ok, -ENOSPC if space was exhausted
 248 */
 249int trace_list_calls(void *buff, size_t buff_size, size_t *needed)
 250{
 251        struct trace_output_hdr *output_hdr = NULL;
 252        void *end, *ptr = buff;
 253        size_t rec, upto;
 254        size_t count;
 255
 256        end = buff ? buff + buff_size : NULL;
 257
 258        /* Place some header information */
 259        if (ptr + sizeof(struct trace_output_hdr) < end)
 260                output_hdr = ptr;
 261        ptr += sizeof(struct trace_output_hdr);
 262
 263        /* Add information about each call */
 264        count = hdr->ftrace_count;
 265        if (count > hdr->ftrace_size)
 266                count = hdr->ftrace_size;
 267        for (rec = upto = 0; rec < count; rec++) {
 268                if (ptr + sizeof(struct trace_call) < end) {
 269                        struct trace_call *call = &hdr->ftrace[rec];
 270                        struct trace_call *out = ptr;
 271
 272                        out->func = call->func * FUNC_SITE_SIZE;
 273                        out->caller = call->caller * FUNC_SITE_SIZE;
 274                        out->flags = call->flags;
 275                        upto++;
 276                }
 277                ptr += sizeof(struct trace_call);
 278        }
 279
 280        /* Update the header */
 281        if (output_hdr) {
 282                output_hdr->rec_count = upto;
 283                output_hdr->type = TRACE_CHUNK_CALLS;
 284        }
 285
 286        /* Work out how must of the buffer we used */
 287        *needed = ptr - buff;
 288        if (ptr > end)
 289                return -ENOSPC;
 290
 291        return 0;
 292}
 293
 294/**
 295 * trace_print_stats() - print basic information about tracing
 296 */
 297void trace_print_stats(void)
 298{
 299        ulong count;
 300
 301#ifndef FTRACE
 302        puts("Warning: make U-Boot with FTRACE to enable function instrumenting.\n");
 303        puts("You will likely get zeroed data here\n");
 304#endif
 305        if (!trace_inited) {
 306                printf("Trace is disabled\n");
 307                return;
 308        }
 309        print_grouped_ull(hdr->func_count, 10);
 310        puts(" function sites\n");
 311        print_grouped_ull(hdr->call_count, 10);
 312        puts(" function calls\n");
 313        print_grouped_ull(hdr->untracked_count, 10);
 314        puts(" untracked function calls\n");
 315        count = min(hdr->ftrace_count, hdr->ftrace_size);
 316        print_grouped_ull(count, 10);
 317        puts(" traced function calls");
 318        if (hdr->ftrace_count > hdr->ftrace_size) {
 319                printf(" (%lu dropped due to overflow)",
 320                       hdr->ftrace_count - hdr->ftrace_size);
 321        }
 322        puts("\n");
 323        printf("%15d maximum observed call depth\n", hdr->max_depth);
 324        printf("%15d call depth limit\n", hdr->depth_limit);
 325        print_grouped_ull(hdr->ftrace_too_deep_count, 10);
 326        puts(" calls not traced due to depth\n");
 327}
 328
 329void __attribute__((no_instrument_function)) trace_set_enabled(int enabled)
 330{
 331        trace_enabled = enabled != 0;
 332}
 333
 334/**
 335 * trace_init() - initialize the tracing system and enable it
 336 *
 337 * @buff:       Pointer to trace buffer
 338 * @buff_size:  Size of trace buffer
 339 * Return:      0 if ok
 340 */
 341int __attribute__((no_instrument_function)) trace_init(void *buff,
 342                size_t buff_size)
 343{
 344        ulong func_count = gd->mon_len / FUNC_SITE_SIZE;
 345        size_t needed;
 346        int was_disabled = !trace_enabled;
 347
 348        trace_save_gd();
 349
 350        if (!was_disabled) {
 351#ifdef CONFIG_TRACE_EARLY
 352                char *end;
 353                ulong used;
 354
 355                /*
 356                 * Copy over the early trace data if we have it. Disable
 357                 * tracing while we are doing this.
 358                 */
 359                trace_enabled = 0;
 360                hdr = map_sysmem(CONFIG_TRACE_EARLY_ADDR,
 361                                 CONFIG_TRACE_EARLY_SIZE);
 362                end = (char *)&hdr->ftrace[min(hdr->ftrace_count,
 363                                               hdr->ftrace_size)];
 364                used = end - (char *)hdr;
 365                printf("trace: copying %08lx bytes of early data from %x to %08lx\n",
 366                       used, CONFIG_TRACE_EARLY_ADDR,
 367                       (ulong)map_to_sysmem(buff));
 368                memcpy(buff, hdr, used);
 369#else
 370                puts("trace: already enabled\n");
 371                return -EALREADY;
 372#endif
 373        }
 374        hdr = (struct trace_hdr *)buff;
 375        needed = sizeof(*hdr) + func_count * sizeof(uintptr_t);
 376        if (needed > buff_size) {
 377                printf("trace: buffer size %zd bytes: at least %zd needed\n",
 378                       buff_size, needed);
 379                return -ENOSPC;
 380        }
 381
 382        if (was_disabled)
 383                memset(hdr, '\0', needed);
 384        hdr->func_count = func_count;
 385        hdr->call_accum = (uintptr_t *)(hdr + 1);
 386
 387        /* Use any remaining space for the timed function trace */
 388        hdr->ftrace = (struct trace_call *)(buff + needed);
 389        hdr->ftrace_size = (buff_size - needed) / sizeof(*hdr->ftrace);
 390        add_textbase();
 391
 392        puts("trace: enabled\n");
 393        hdr->depth_limit = CONFIG_TRACE_CALL_DEPTH_LIMIT;
 394        trace_enabled = 1;
 395        trace_inited = 1;
 396
 397        return 0;
 398}
 399
 400#ifdef CONFIG_TRACE_EARLY
 401/**
 402 * trace_early_init() - initialize the tracing system for early tracing
 403 *
 404 * Return:      0 if ok, -ENOSPC if not enough memory is available
 405 */
 406int __attribute__((no_instrument_function)) trace_early_init(void)
 407{
 408        ulong func_count = gd->mon_len / FUNC_SITE_SIZE;
 409        size_t buff_size = CONFIG_TRACE_EARLY_SIZE;
 410        size_t needed;
 411
 412        /* We can ignore additional calls to this function */
 413        if (trace_enabled)
 414                return 0;
 415
 416        hdr = map_sysmem(CONFIG_TRACE_EARLY_ADDR, CONFIG_TRACE_EARLY_SIZE);
 417        needed = sizeof(*hdr) + func_count * sizeof(uintptr_t);
 418        if (needed > buff_size) {
 419                printf("trace: buffer size is %zd bytes, at least %zd needed\n",
 420                       buff_size, needed);
 421                return -ENOSPC;
 422        }
 423
 424        memset(hdr, '\0', needed);
 425        hdr->call_accum = (uintptr_t *)(hdr + 1);
 426        hdr->func_count = func_count;
 427
 428        /* Use any remaining space for the timed function trace */
 429        hdr->ftrace = (struct trace_call *)((char *)hdr + needed);
 430        hdr->ftrace_size = (buff_size - needed) / sizeof(*hdr->ftrace);
 431        add_textbase();
 432        hdr->depth_limit = CONFIG_TRACE_EARLY_CALL_DEPTH_LIMIT;
 433        printf("trace: early enable at %08x\n", CONFIG_TRACE_EARLY_ADDR);
 434
 435        trace_enabled = 1;
 436
 437        return 0;
 438}
 439#endif
 440