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