1/*
2 * Copyright 2014, General Dynamics C4 Systems
3 *
4 * SPDX-License-Identifier: GPL-2.0-only
5 */
6
7/*
8 * Kernel Profiler
9 *
10 * 2007 David Greenaway
11 * 2007 Ported to seL4 C kernel by Philip Derrin
12 */
13
14#ifdef PROFILER
15
16#include <util.h>
17#include <machine.h>
18#include <machine/profiler.h>
19
20#ifdef CHECKPOINT_PROFILER
21/* The current checkpoint value */
22volatile unsigned int checkpoint VISIBLE;
23unsigned int max_checkpoint;
24
25/* Event count for each checkpoint value */
26profiler_entry_t profiler_entries[MAX_UNIQUE_CHECKPOINTS];
27#else
28/* Number of entries the profiler currently keeps track of */
29int profiler_num_entries;
30
31/* Number of instructions the profiler could not record */
32long long profiler_dropped_instructions;
33
34/* The instructions recorded by the profiler */
35profiler_entry_t profiler_entries[MAX_UNIQUE_INSTRUCTIONS];
36#endif
37
38/* Should we be profiling the system? */
39bool_t profiler_enabled VISIBLE = true;
40
41#ifdef CHECKPOINT_PROFILER
42void profiler_reset(void)
43{
44    word_t i;
45
46    for (i = 0; i < MAX_UNIQUE_CHECKPOINTS; i++) {
47        profiler_entries[i].pc = 0;
48        profiler_entries[i].count = 0;
49    }
50    checkpoint = 0;
51}
52
53void profiler_list(void)
54{
55    unsigned int samples, i, count;
56
57    printf("checkpoint count\n");
58
59    samples = 0;
60    count = 0;
61    for (i = 0; i <= max_checkpoint; i++) {
62        if (profiler_entries[i].pc != 0) {
63            printf("%u %u\n", i, (unsigned int)profiler_entries[i].count);
64            samples += profiler_entries[i].count;
65            count++;
66        }
67    }
68
69    printf("%u checkpoints, %u sample(s)\n", count, samples);
70}
71
72void profiler_record_sample(word_t pc)
73{
74    if (checkpoint > max_checkpoint) {
75        max_checkpoint = checkpoint;
76    }
77
78    if (!profiler_entries[checkpoint].pc) {
79        profiler_entries[checkpoint].pc = 1;
80    }
81    profiler_entries[checkpoint].count++;
82}
83#else
84/*
85 * Reset all counters
86 */
87void profiler_reset(void)
88{
89    for (word_t i = 0; i < MAX_UNIQUE_INSTRUCTIONS; i++) {
90        profiler_entries[i].pc = 0;
91        profiler_entries[i].count = 0;
92    }
93    profiler_num_entries = 0;
94    profiler_dropped_instructions = 0;
95}
96
97/*
98 * Dump out recorded values to stdout
99 */
100void profiler_list(void)
101{
102    long long samples;
103
104    /* Print header */
105    printf("addr     count\n");
106
107    /* Print out each address */
108    samples = 0;
109    for (word_t i = 0; i < MAX_UNIQUE_INSTRUCTIONS; i++) {
110        if (profiler_entries[i].pc != 0) {
111            printf("%x %d\n", (unsigned int)profiler_entries[i].pc,
112                   (int)profiler_entries[i].count);
113            samples += profiler_entries[i].count;
114        }
115    }
116
117    /* Print statistics */
118    printf("\n%d unique address(es), %d sample(s)\n",
119           (int)profiler_num_entries, (int)samples);
120    if (profiler_dropped_instructions > 0) {
121        printf("*** WARNING : %d instructions dropped\n",
122               (int)profiler_dropped_instructions);
123    }
124}
125
126/*
127 * Record a sample
128 */
129void profiler_record_sample(word_t pc)
130{
131    /* Number used for hashing such that the gcd of MAX_UNIQUE_INSTRUCTIONS and
132     * (1 .. hashVal) is 1.
133     *
134     * As MAX_UNIQUE_INSTRUCTIONS is prime, this can be ensured mearly by
135     * having hashVal < MAX_UNIQUE_INSTRUCTIONS. */
136    const int hashVal = 1024;
137
138    /* Hash optimised for valid ARM instruction addresses, which are always
139     * word aligned. */
140    word_t hash = (pc >> 2) % MAX_UNIQUE_INSTRUCTIONS;
141    word_t hash2 = ((pc >> 2) % hashVal) + 1;
142
143    if (!profiler_enabled) {
144        return;
145    }
146
147    while (true) {
148
149        if (profiler_entries[hash].pc == pc) {
150
151            /* Found the correct entry */
152            profiler_entries[hash].count++;
153            break;
154
155        } else if (profiler_entries[hash].pc == 0) {
156
157            /* Found a spot for a new entry */
158            if (profiler_num_entries < (MAX_UNIQUE_INSTRUCTIONS / 4) * 3) {
159                profiler_entries[hash].pc = pc;
160                profiler_entries[hash].count = 1;
161                profiler_num_entries++;
162                break;
163            } else {
164                /* Too many entries. Abort the record. */
165                profiler_dropped_instructions++;
166                break;
167            }
168        }
169
170        /* Keep searching */
171        hash += hash2;
172        hash %= MAX_UNIQUE_INSTRUCTIONS;
173    }
174}
175#endif
176
177#endif /* CONFIG_KDB_PROFILER */
178