Contiki 2.5
profiling.c
1 /*
2  * Copyright (c) 2011, Daniel Willmann <daniel@totalueberwachung.de>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  * notice, this list of conditions and the following disclaimer in the
12  * documentation and/or other materials provided with the distribution.
13  * 3. Neither the name of the Institute nor the names of its contributors
14  * may be used to endorse or promote products derived from this software
15  * without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  *
29  * This file is part of the Contiki operating system.
30  *
31  * Compiler assisted profiler for Contiki - needs gcc -finstrument-functions
32  *
33  * @(#)$$
34  */
35 
36 #include <stdio.h>
37 #include <string.h>
38 
39 #include "contiki.h"
40 #include "sys/profiling.h"
41 #include "profiling_arch.h"
42 
43 /* Disable by default */
44 #ifdef PROFILES_CONF_MAX
45 #define MAX_PROFILES PROFILES_CONF_MAX
46 #else
47 #define MAX_PROFILES 0
48 #endif /* MAX_CONF_PROFILES */
49 
50 /* Disable by default */
51 #ifdef PROFILES_CONF_STACKSIZE
52 #define PROFILE_STACKSIZE PROFILES_CONF_STACKSIZE
53 #else
54 #define PROFILE_STACKSIZE 0
55 #endif /* PROFILES_CONF_STACKSIZE */
56 
57 static struct profile_t profile;
58 static struct profile_site_t site[MAX_PROFILES];
59 static int fine_count;
60 
61 static struct profile_callstack_t callstack[PROFILE_STACKSIZE] __attribute__ ((section (".noinit")));
62 static int stacklevel __attribute__ ((section (".noinit")));
63 
64 void profiling_stack_trace(void)
65 {
66  int i;
67  if ((stacklevel <= 0) || (stacklevel > PROFILE_STACKSIZE)) {
68  printf("Stack corrupted or not recorded.\n");
69  return;
70  }
71 
72  printf("Stacktrace: %i frames, %lu ticks/s\n", stacklevel, CLOCK_SECOND*256l);
73 
74  for (i=0; i<stacklevel; i++) {
75  printf("%i: %p->%p @%lu\n", i, ARCHADDR2ADDR(callstack[i].caller), ARCHADDR2ADDR(callstack[i].func), callstack[i].time_start);
76  }
77  printf("\n");
78 }
79 
80 static inline void profiling_internal(uint8_t internal) __attribute__ ((no_instrument_function));
81 static inline void profiling_internal(uint8_t internal)
82 {
83  if (internal)
84  profile.status |= PROFILING_INTERNAL;
85  else
86  profile.status &= ~PROFILING_INTERNAL;
87 }
88 
89 void profiling_report(const char *name, uint8_t pretty)
90 {
91  int i;
92 
93  /* The parser would be confused if the name contains colons or newlines, so disallow */
94  if (!name || strchr(name, ':') || strchr(name, '\r') || strchr(name, '\n')) {
95  printf("The profile report name is invalid\n");
96  name = "invalid";
97  }
98 
99  if (pretty)
100  printf("PROF: \"%s\" %u sites %u max sites %lu ticks spent %lu ticks/s\nfrom:to:calls:time\n", name, profile.num_sites, profile.max_sites, profile.time_run, CLOCK_SECOND*256l);
101  else
102  printf("PROF:%s:%u:%u:%lu:%lu\n", name, profile.num_sites, profile.max_sites, profile.time_run, CLOCK_SECOND*256l);
103 
104  for(i=0; i<profile.num_sites;i++) {
105  printf("%p:%p:%lu:%lu:%u:%u\n", ARCHADDR2ADDR(profile.sites[i].from), ARCHADDR2ADDR(profile.sites[i].addr),
106  profile.sites[i].calls, profile.sites[i].time_accum, profile.sites[i].time_min, profile.sites[i].time_max);
107  }
108  printf("\n");
109 }
110 
111 struct profile_t *profiling_get()
112 {
113  return &profile;
114 }
115 
116 void profiling_init(void)
117 {
118  fine_count = clock_fine_max() + 1;
119 
120  profile.sites = site;
121  profile.max_sites = MAX_PROFILES;
122  profile.num_sites = 0;
123  profile.time_run = 0;
124  profile.status = 0;
125  stacklevel = 0;
126 }
127 
128 void profiling_start(void)
129 {
130  clock_time_t now;
131  unsigned short now_fine;
132 
133  if (profile.status & PROFILING_STARTED)
134  return;
135 
136  do {
137  now_fine = clock_fine();
138  now = clock_time();
139  } while (now_fine != clock_fine());
140 
141 
142  profile.time_start = ((unsigned long)now<<8) + now_fine*256/fine_count;
143  profile.status |= PROFILING_STARTED;
144 
145  /* Reset the callstack */
146  stacklevel = 0;
147 }
148 
149 void profiling_stop(void)
150 {
151  unsigned long temp;
152  clock_time_t now;
153  unsigned short now_fine;
154 
155  if (!profile.status & PROFILING_STARTED)
156  return;
157 
158  do {
159  now_fine = clock_fine();
160  now = clock_time();
161  } while (now_fine != clock_fine());
162 
163 
164  temp = ((unsigned long)now<<8) + now_fine*256/fine_count;
165 
166  profile.time_run += (temp - profile.time_start);
167  profile.status &= ~PROFILING_STARTED;
168 }
169 
170 /* Don't instrument the instrumentation functions */
171 void __cyg_profile_func_enter(void *, void *) __attribute__ ((no_instrument_function));
172 void __cyg_profile_func_exit(void *, void *) __attribute__ ((no_instrument_function));
173 static inline struct profile_site_t *find_or_add_site(void *func, void *caller, uint8_t findonly) __attribute__ ((no_instrument_function));
174 
175 static inline struct profile_site_t *find_or_add_site(void *func, void *caller, uint8_t findonly)
176 {
177  struct profile_site_t *site = NULL;
178  int16_t lower, upper;
179  uint16_t newindex = 0, i;
180 
181  lower = 0;
182  upper = profile.num_sites-1;
183 
184  while (lower <= upper) {
185  newindex = ((upper - lower)>>1) + lower;
186  site = &profile.sites[newindex];
187  if (func > site->addr) {
188  lower = newindex + 1;
189  } else if (func < site->addr) {
190  upper = newindex - 1;
191  } else {
192  if (caller > site->from) {
193  lower = newindex + 1;
194  } else if (caller < site->from) {
195  upper = newindex - 1;
196  } else {
197  return site;
198  }
199  }
200  }
201 
202  /* We only want to get an entry if it existed */
203  if (findonly)
204  return NULL;
205 
206  /* Table is full - nothing we can do */
207  if (profile.num_sites == profile.max_sites)
208  return NULL;
209 
210  newindex = lower;
211  /* Site not found, need to insert it */
212  for (i = profile.num_sites; i>newindex; i--) {
213  memcpy(&profile.sites[i], &profile.sites[i-1], sizeof(struct profile_site_t));
214  }
215  profile.num_sites++;
216  site = &profile.sites[newindex];
217  site->from = caller;
218  site->addr = func;
219  site->calls = 0;
220  site->time_max = 0;
221  site->time_min = 0xFFFF;
222  site->time_accum = 0;
223 
224  return site;
225 }
226 
227 void __cyg_profile_func_enter(void *func, void *caller)
228 {
229  struct profile_site_t *site;
230  clock_time_t now;
231  unsigned short now_fine;
232 
233  if (!(profile.status&PROFILING_STARTED) || (profile.status&PROFILING_INTERNAL))
234  return;
235 
236  profiling_internal(1);
237 
238  if (stacklevel >= PROFILE_STACKSIZE)
239  goto out;
240 
241  site = find_or_add_site(func, caller, 0);
242  if (!site)
243  goto out;
244 
245  /* Update the call stack */
246  do {
247  now_fine = clock_fine();
248  now = clock_time();
249  } while (now_fine != clock_fine());
250 
251  callstack[stacklevel].time_start = ((unsigned long)now<<8) + now_fine*256/fine_count;
252  callstack[stacklevel].func = func;
253  callstack[stacklevel].caller = caller;
254 
255  stacklevel++;
256 
257 out:
258  profiling_internal(0);
259 }
260 
261 void __cyg_profile_func_exit(void *func, void *caller)
262 {
263  unsigned long temp;
264  struct profile_site_t *site;
265  clock_time_t now;
266  unsigned short now_fine;
267 
268  if (!(profile.status&PROFILING_STARTED) || (profile.status&PROFILING_INTERNAL))
269  return;
270 
271  profiling_internal(1);
272 
273  do {
274  now_fine = clock_fine();
275  now = clock_time();
276  } while (now_fine != clock_fine());
277 
278  temp = ((unsigned long)now<<8) + now_fine*256/fine_count;
279 
280  /* See if this call was recorded on the call stack */
281  if (stacklevel > 0 && callstack[stacklevel-1].func == func &&
282  callstack[stacklevel-1].caller == caller) {
283  temp = temp - callstack[stacklevel-1].time_start;
284  stacklevel--;
285  } else {
286  goto out;
287  }
288 
289  site = find_or_add_site(func, caller, 1);
290  if (!site)
291  goto out;
292 
293  /* Update calls and time */
294  site->calls++;
295  site->time_accum += temp;
296 
297  /* Min max calculation */
298  if (temp > site->time_max) {
299  site->time_max = temp;
300  if (temp > 0xFFFF)
301  site->time_max = 0xFFFF;
302  }
303  if (temp < site->time_min)
304  site->time_min = temp;
305 
306  profiling_internal(0);
307  return;
308 
309 out:
310  /* We didn't find the entry, abort */
311  profiling_internal(0);
312 }
313 
314