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