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 * This is called on every function entry
 134 *
 135 * We add to our tally for this function and add to the list of called
 136 * functions.
 137 *
 138 * @param func_ptr      Pointer to function being entered
 139 * @param 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 * This is called on every function exit
 165 *
 166 * We do nothing here.
 167 *
 168 * @param func_ptr      Pointer to function being entered
 169 * @param caller        Pointer to function which called this function
 170 */
 171void __attribute__((no_instrument_function)) __cyg_profile_func_exit(
 172                void *func_ptr, void *caller)
 173{
 174        if (trace_enabled) {
 175                trace_swap_gd();
 176                add_ftrace(func_ptr, caller, FUNCF_EXIT);
 177                hdr->depth--;
 178                trace_swap_gd();
 179        }
 180}
 181
 182/**
 183 * Produce a list of called functions
 184 *
 185 * The information is written into the supplied buffer - a header followed
 186 * by a list of function records.
 187 *
 188 * @param buff          Buffer to place list into
 189 * @param buff_size     Size of buffer
 190 * @param needed        Returns size of buffer needed, which may be
 191 *                      greater than buff_size if we ran out of space.
 192 * @return 0 if ok, -1 if space was exhausted
 193 */
 194int trace_list_functions(void *buff, size_t buff_size, size_t *needed)
 195{
 196        struct trace_output_hdr *output_hdr = NULL;
 197        void *end, *ptr = buff;
 198        size_t func;
 199        size_t upto;
 200
 201        end = buff ? buff + buff_size : NULL;
 202
 203        /* Place some header information */
 204        if (ptr + sizeof(struct trace_output_hdr) < end)
 205                output_hdr = ptr;
 206        ptr += sizeof(struct trace_output_hdr);
 207
 208        /* Add information about each function */
 209        for (func = upto = 0; func < hdr->func_count; func++) {
 210                size_t calls = hdr->call_accum[func];
 211
 212                if (!calls)
 213                        continue;
 214
 215                if (ptr + sizeof(struct trace_output_func) < end) {
 216                        struct trace_output_func *stats = ptr;
 217
 218                        stats->offset = func * FUNC_SITE_SIZE;
 219                        stats->call_count = calls;
 220                        upto++;
 221                }
 222                ptr += sizeof(struct trace_output_func);
 223        }
 224
 225        /* Update the header */
 226        if (output_hdr) {
 227                output_hdr->rec_count = upto;
 228                output_hdr->type = TRACE_CHUNK_FUNCS;
 229        }
 230
 231        /* Work out how must of the buffer we used */
 232        *needed = ptr - buff;
 233        if (ptr > end)
 234                return -ENOSPC;
 235
 236        return 0;
 237}
 238
 239int trace_list_calls(void *buff, size_t buff_size, size_t *needed)
 240{
 241        struct trace_output_hdr *output_hdr = NULL;
 242        void *end, *ptr = buff;
 243        size_t rec, upto;
 244        size_t count;
 245
 246        end = buff ? buff + buff_size : NULL;
 247
 248        /* Place some header information */
 249        if (ptr + sizeof(struct trace_output_hdr) < end)
 250                output_hdr = ptr;
 251        ptr += sizeof(struct trace_output_hdr);
 252
 253        /* Add information about each call */
 254        count = hdr->ftrace_count;
 255        if (count > hdr->ftrace_size)
 256                count = hdr->ftrace_size;
 257        for (rec = upto = 0; rec < count; rec++) {
 258                if (ptr + sizeof(struct trace_call) < end) {
 259                        struct trace_call *call = &hdr->ftrace[rec];
 260                        struct trace_call *out = ptr;
 261
 262                        out->func = call->func * FUNC_SITE_SIZE;
 263                        out->caller = call->caller * FUNC_SITE_SIZE;
 264                        out->flags = call->flags;
 265                        upto++;
 266                }
 267                ptr += sizeof(struct trace_call);
 268        }
 269
 270        /* Update the header */
 271        if (output_hdr) {
 272                output_hdr->rec_count = upto;
 273                output_hdr->type = TRACE_CHUNK_CALLS;
 274        }
 275
 276        /* Work out how must of the buffer we used */
 277        *needed = ptr - buff;
 278        if (ptr > end)
 279                return -ENOSPC;
 280
 281        return 0;
 282}
 283
 284/* Print basic information about tracing */
 285void trace_print_stats(void)
 286{
 287        ulong count;
 288
 289#ifndef FTRACE
 290        puts("Warning: make U-Boot with FTRACE to enable function instrumenting.\n");
 291        puts("You will likely get zeroed data here\n");
 292#endif
 293        if (!trace_inited) {
 294                printf("Trace is disabled\n");
 295                return;
 296        }
 297        print_grouped_ull(hdr->func_count, 10);
 298        puts(" function sites\n");
 299        print_grouped_ull(hdr->call_count, 10);
 300        puts(" function calls\n");
 301        print_grouped_ull(hdr->untracked_count, 10);
 302        puts(" untracked function calls\n");
 303        count = min(hdr->ftrace_count, hdr->ftrace_size);
 304        print_grouped_ull(count, 10);
 305        puts(" traced function calls");
 306        if (hdr->ftrace_count > hdr->ftrace_size) {
 307                printf(" (%lu dropped due to overflow)",
 308                       hdr->ftrace_count - hdr->ftrace_size);
 309        }
 310        puts("\n");
 311        printf("%15d maximum observed call depth\n", hdr->max_depth);
 312        printf("%15d call depth limit\n", hdr->depth_limit);
 313        print_grouped_ull(hdr->ftrace_too_deep_count, 10);
 314        puts(" calls not traced due to depth\n");
 315}
 316
 317void __attribute__((no_instrument_function)) trace_set_enabled(int enabled)
 318{
 319        trace_enabled = enabled != 0;
 320}
 321
 322/**
 323 * Init the tracing system ready for used, and enable it
 324 *
 325 * @param buff          Pointer to trace buffer
 326 * @param buff_size     Size of trace buffer
 327 */
 328int __attribute__((no_instrument_function)) trace_init(void *buff,
 329                size_t buff_size)
 330{
 331        ulong func_count = gd->mon_len / FUNC_SITE_SIZE;
 332        size_t needed;
 333        int was_disabled = !trace_enabled;
 334
 335        trace_save_gd();
 336
 337        if (!was_disabled) {
 338#ifdef CONFIG_TRACE_EARLY
 339                char *end;
 340                ulong used;
 341
 342                /*
 343                 * Copy over the early trace data if we have it. Disable
 344                 * tracing while we are doing this.
 345                 */
 346                trace_enabled = 0;
 347                hdr = map_sysmem(CONFIG_TRACE_EARLY_ADDR,
 348                                 CONFIG_TRACE_EARLY_SIZE);
 349                end = (char *)&hdr->ftrace[min(hdr->ftrace_count,
 350                                               hdr->ftrace_size)];
 351                used = end - (char *)hdr;
 352                printf("trace: copying %08lx bytes of early data from %x to %08lx\n",
 353                       used, CONFIG_TRACE_EARLY_ADDR,
 354                       (ulong)map_to_sysmem(buff));
 355                memcpy(buff, hdr, used);
 356#else
 357                puts("trace: already enabled\n");
 358                return -EALREADY;
 359#endif
 360        }
 361        hdr = (struct trace_hdr *)buff;
 362        needed = sizeof(*hdr) + func_count * sizeof(uintptr_t);
 363        if (needed > buff_size) {
 364                printf("trace: buffer size %zd bytes: at least %zd needed\n",
 365                       buff_size, needed);
 366                return -ENOSPC;
 367        }
 368
 369        if (was_disabled)
 370                memset(hdr, '\0', needed);
 371        hdr->func_count = func_count;
 372        hdr->call_accum = (uintptr_t *)(hdr + 1);
 373
 374        /* Use any remaining space for the timed function trace */
 375        hdr->ftrace = (struct trace_call *)(buff + needed);
 376        hdr->ftrace_size = (buff_size - needed) / sizeof(*hdr->ftrace);
 377        add_textbase();
 378
 379        puts("trace: enabled\n");
 380        hdr->depth_limit = CONFIG_TRACE_CALL_DEPTH_LIMIT;
 381        trace_enabled = 1;
 382        trace_inited = 1;
 383
 384        return 0;
 385}
 386
 387#ifdef CONFIG_TRACE_EARLY
 388int __attribute__((no_instrument_function)) trace_early_init(void)
 389{
 390        ulong func_count = gd->mon_len / FUNC_SITE_SIZE;
 391        size_t buff_size = CONFIG_TRACE_EARLY_SIZE;
 392        size_t needed;
 393
 394        /* We can ignore additional calls to this function */
 395        if (trace_enabled)
 396                return 0;
 397
 398        hdr = map_sysmem(CONFIG_TRACE_EARLY_ADDR, CONFIG_TRACE_EARLY_SIZE);
 399        needed = sizeof(*hdr) + func_count * sizeof(uintptr_t);
 400        if (needed > buff_size) {
 401                printf("trace: buffer size is %zd bytes, at least %zd needed\n",
 402                       buff_size, needed);
 403                return -ENOSPC;
 404        }
 405
 406        memset(hdr, '\0', needed);
 407        hdr->call_accum = (uintptr_t *)(hdr + 1);
 408        hdr->func_count = func_count;
 409
 410        /* Use any remaining space for the timed function trace */
 411        hdr->ftrace = (struct trace_call *)((char *)hdr + needed);
 412        hdr->ftrace_size = (buff_size - needed) / sizeof(*hdr->ftrace);
 413        add_textbase();
 414        hdr->depth_limit = CONFIG_TRACE_EARLY_CALL_DEPTH_LIMIT;
 415        printf("trace: early enable at %08x\n", CONFIG_TRACE_EARLY_ADDR);
 416
 417        trace_enabled = 1;
 418
 419        return 0;
 420}
 421#endif
 422