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 */
22 volatile unsigned int checkpoint VISIBLE;
23 unsigned int max_checkpoint;
24
25 /* Event count for each checkpoint value */
26 profiler_entry_t profiler_entries[MAX_UNIQUE_CHECKPOINTS];
27 #else
28 /* Number of entries the profiler currently keeps track of */
29 int profiler_num_entries;
30
31 /* Number of instructions the profiler could not record */
32 long long profiler_dropped_instructions;
33
34 /* The instructions recorded by the profiler */
35 profiler_entry_t profiler_entries[MAX_UNIQUE_INSTRUCTIONS];
36 #endif
37
38 /* Should we be profiling the system? */
39 bool_t profiler_enabled VISIBLE = true;
40
41 #ifdef CHECKPOINT_PROFILER
profiler_reset(void)42 void 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
profiler_list(void)53 void 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
profiler_record_sample(word_t pc)72 void 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 */
profiler_reset(void)87 void 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 */
profiler_list(void)100 void 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 */
profiler_record_sample(word_t pc)129 void 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