Initial implementation of a profiling system for Contiki

This commit is contained in:
adamdunkels 2007-10-23 20:39:07 +00:00
parent d3319f71b3
commit 0fdb3e3ddc
3 changed files with 573 additions and 0 deletions

View file

@ -0,0 +1,244 @@
/*
* Copyright (c) 2007, Swedish Institute of Computer Science.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the Institute nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* This file is part of the Contiki operating system.
*
* $Id: profile-aggregates.c,v 1.1 2007/10/23 20:39:07 adamdunkels Exp $
*/
/**
* \file
* Compuation of aggregates for the Contiki profiling system
* \author
* Adam Dunkels <adam@sics.se>
*/
#include "sys/profile.h"
#include <stdlib.h>
#include <stdio.h>
struct aggregate {
const unsigned char *ptr;
unsigned short episodes;
unsigned long cycles;
};
#define DETAILED_AGGREGATES 0
#define MAX_CATEGORIES 32
#define LIST_LEN 100
static struct aggregate aggregates[LIST_LEN];
static int aggregates_list_ptr = 0;
/*---------------------------------------------------------------------------*/
static struct aggregate *
find_aggregate_category(const uint16_t cat)
{
int i;
uint16_t acat;
/* printf("find_aggregate_category 0x%04x %c%c\n", */
/* cat, cat >> 8, cat & 0xff); */
for(i = 0; i < aggregates_list_ptr; ++i) {
acat = (aggregates[i].ptr[0] << 8) + aggregates[i].ptr[1];
/* printf("acat 0x%04x %c%c\n", */
/* acat, acat >> 8, acat & 0xff); */
if(acat == cat) {
return &aggregates[i];
}
}
if(i == LIST_LEN) {
return NULL;
}
aggregates[aggregates_list_ptr].ptr = NULL;
return &aggregates[aggregates_list_ptr++];
}
/*---------------------------------------------------------------------------*/
static struct aggregate *
find_aggregate(const unsigned char *ptr)
{
int i;
for(i = 0; i < aggregates_list_ptr; ++i) {
if(aggregates[i].ptr == ptr) {
return &aggregates[i];
}
}
if(i == LIST_LEN) {
return NULL;
}
return &aggregates[aggregates_list_ptr++];
}
/*---------------------------------------------------------------------------*/
void
profile_aggregates_print(void)
{
int i;
#if DETAILED_AGGREGATES
for(i = 0; i < aggregates_list_ptr; ++i) {
printf("-- %s: %lu / %u = %lu\n", aggregates[i].ptr,
aggregates[i].cycles,
aggregates[i].episodes,
aggregates[i].cycles / aggregates[i].episodes);
}
#else
for(i = 0; i < aggregates_list_ptr; ++i) {
printf("-- %c%c: %lu / %u = %lu\n",
aggregates[i].ptr[0], aggregates[i].ptr[1],
aggregates[i].cycles,
aggregates[i].episodes,
aggregates[i].cycles / aggregates[i].episodes);
}
#endif
printf("Memory for aggregates: %d * %d = %d\n",
sizeof(struct aggregate), aggregates_list_ptr,
sizeof(struct aggregate) * aggregates_list_ptr);
}
/*---------------------------------------------------------------------------*/
static void
detailed_profile_aggregates_compute(void)
{
int i;
rtimer_clock_t t;
/* const char *str = "profile_aggregates_compute";
PROFILE_TIMESTAMP(str);*/
t = profile_timestamps[0].time;
for(i = 1; i < PROFILE_TIMESTAMP_PTR; ++i) {
struct aggregate *a;
a = find_aggregate(profile_timestamps[i - 1].ptr);
if(a == NULL) {
/* The list is full, skip this entry */
printf("profile_aggregates_compute: list full\n");
} else if(a->ptr == NULL) {
a->ptr = profile_timestamps[i - 1].ptr;
a->cycles = (unsigned long)(profile_timestamps[i].time - t);
a->episodes = 1;
} else {
a->cycles += (unsigned long)(profile_timestamps[i].time - t);
a->episodes++;
}
t = profile_timestamps[i].time;
}
/* PROFILE_TIMESTAMP(str);*/
/*printf("Aggregating time %u, len %d, list len %d, overhead %d\n",
profile_timediff(str, str), PROFILE_TIMESTAMP_PTR,
aggregates_list_ptr, profile_timestamp_time);*/
/* print_aggregates();*/
}
/*---------------------------------------------------------------------------*/
static void
category_profile_aggregates_compute(void)
{
int i,j;
rtimer_clock_t t;
uint16_t categories[MAX_CATEGORIES];
int categories_ptr = 0;
/* const char *str = "profile_aggregates_compute";
PROFILE_TIMESTAMP(str);*/
t = profile_timestamps[0].time;
for(i = 1; i < PROFILE_TIMESTAMP_PTR; ++i) {
struct aggregate *a;
uint16_t cat;
/* printf("category_profile_aggregates_compute %s\n", */
/* profile_timestamps[i - 1].ptr); */
cat = (profile_timestamps[i - 1].ptr[0] << 8) +
(profile_timestamps[i - 1].ptr[1] & 0xff);
a = find_aggregate_category(cat);
if(a == NULL) {
/* The list is full, skip this entry */
printf("profile_aggregates_compute: list full\n");
} else if(a->ptr == NULL) {
a->ptr = profile_timestamps[i - 1].ptr;
a->cycles = (unsigned long)(profile_timestamps[i].time - t - profile_timestamp_time);
a->episodes = 1;
} else {
a->cycles += (unsigned long)(profile_timestamps[i].time - t - profile_timestamp_time);
/* Make sure that we only update the episodes of each category
once per run. We keep track of all updated categories in the
"categories" array. If the category is already present in the
array, we do not update it. Otherwise, we insert the category
in the array and update the episodes counter of the
category. */
for(j = 0; j < categories_ptr; ++j) {
if(categories[j] == cat) {
break;
}
}
if(j == categories_ptr) {
categories[j] = cat;
categories_ptr++;
a->episodes++;
}
}
t = profile_timestamps[i].time;
}
/* PROFILE_TIMESTAMP(str);*/
/*printf("Aggregating time %u, len %d, list len %d, overhead %d\n",
profile_timediff(str, str), PROFILE_TIMESTAMP_PTR,
aggregates_list_ptr, profile_timestamp_time);*/
/* print_aggregates();*/
}
/*---------------------------------------------------------------------------*/
void
profile_aggregates_compute(void)
{
#if DETAILED_AGGREGATES
detailed_profile_aggregates_compute();
#else
category_profile_aggregates_compute();
#endif
}
/*---------------------------------------------------------------------------*/

173
core/sys/profile.c Normal file
View file

@ -0,0 +1,173 @@
/*
* Copyright (c) 2007, Swedish Institute of Computer Science.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the Institute nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* This file is part of the Contiki operating system.
*
* $Id: profile.c,v 1.1 2007/10/23 20:39:07 adamdunkels Exp $
*/
/**
* \file
* Implementation of the Contiki profiling system
* \author
* Adam Dunkels <adam@sics.se>
*/
#include "sys/profile.h"
#include <stdlib.h> /* For NULL */
unsigned int profile_timestamp_ptr;
struct profile_timestamp profile_timestamps[PROFILE_LIST_LENGTH];
rtimer_clock_t profile_timestamp_time;
int profile_invalid_episode_overflow, profile_invalid_episode_toolong;
int profile_max_queuelen = 0;
static rtimer_clock_t episode_start_time;
/* The number of fine grained ticks per coarse grained ticks. We
currently (MSP430) have 2457600 ticks per second for the fine
grained timer, and 32678 / 8 ticks per second for the coarse. */
#define FINE_TICKS_PER_COARSE_TICK (2457600/(32678/8))
/*---------------------------------------------------------------------------*/
rtimer_clock_t
profile_timediff(const char *ptr1, const char *ptr2)
{
int i;
int t1, t2;
int timestamp_ptr = PROFILE_TIMESTAMP_PTR;
/* printf("profile_timestamp_ptr %d max %d\n", profile_timestamp_ptr, profile_max_queuelen);*/
t1 = t2 = PROFILE_LIST_LENGTH;
for(i = timestamp_ptr - 1; i >= 0; --i) {
/* printf("Checking 1 %s %u == %s i %d\n",
profile_timestamps[i].ptr,
profile_timestamps[i].time,
ptr1, i);*/
if(profile_timestamps[i].ptr == ptr1) {
t1 = i;
break;
}
}
for(i = i - 1; i >= 0; --i) {
/* printf("Checking 2 %s %u == %s i %d\n",
profile_timestamps[i].ptr,
profile_timestamps[i].time,
ptr1, i);*/
if(profile_timestamps[i].ptr == ptr2) {
t2 = i;
break;
}
}
/* printf("t1 %d t2 %d\n", t1, t2);*/
if(t1 != PROFILE_LIST_LENGTH && t2 != PROFILE_LIST_LENGTH) {
return profile_timestamps[t1].time - profile_timestamps[t2].time;
}
return 0;
}
/*---------------------------------------------------------------------------*/
void
profile_clear_timestamps(void)
{
/* int i;
for(i = 0; i < PROFILE_LIST_LENGTH; ++i) {
profile_timestamps[i].str = "NULL";
profile_timestamps[i].time = 0;
}*/
profile_timestamp_ptr = 0;
}
/*---------------------------------------------------------------------------*/
void
profile_init(void)
{
profile_clear_timestamps();
/* Measure the time for taking a timestamp. */
PROFILE_TIMESTAMP(NULL);
PROFILE_TIMESTAMP(NULL);
profile_timestamp_time = profile_timestamps[1].time - profile_timestamps[0].time;
profile_clear_timestamps();
}
/*---------------------------------------------------------------------------*/
void
profile_episode_start(void)
{
profile_timestamp_ptr = 0;
episode_start_time = clock_counter();
profile_timestamps[PROFILE_LIST_LENGTH - 1].ptr = NULL;
}
/*---------------------------------------------------------------------------*/
void
profile_episode_end(void)
{
rtimer_clock_t episode_end_time = clock_counter();
PROFILE_TIMESTAMP("profile_episode_end");
/* printf("profile_episode_end start %u, end %u, max time %u\n", episode_start_time, episode_end_time, 65536/FINE_TICKS_PER_COARSE_TICK); */
if(profile_timestamps[PROFILE_LIST_LENGTH - 1].ptr != NULL) {
/* Invalid episode because of list overflow. */
profile_invalid_episode_overflow++;
profile_max_queuelen = PROFILE_LIST_LENGTH;
} else if(episode_end_time - episode_start_time > 65536/FINE_TICKS_PER_COARSE_TICK) {
/* Invalid episode because of timer overflow. */
profile_invalid_episode_toolong++;
} else {
/* Compute aggregates. */
if(PROFILE_TIMESTAMP_PTR > profile_max_queuelen) {
profile_max_queuelen = PROFILE_TIMESTAMP_PTR;
}
profile_aggregates_compute();
/* printf("Episode length %d\n", profile_timestamp_ptr);*/
}
/* profile_aggregates_print(); */
/* profile_print_stats(); */
}
/*---------------------------------------------------------------------------*/
void
profile_print_stats(void)
{
printf("Memory for profiling: %d * %d = %d\n",
sizeof(struct profile_timestamp), profile_max_queuelen,
sizeof(struct profile_timestamp) * profile_max_queuelen);
printf("Invalid episodes overflow %d time %d\n",
profile_invalid_episode_overflow,
profile_invalid_episode_toolong);
}
/*---------------------------------------------------------------------------*/

156
core/sys/profile.h Normal file
View file

@ -0,0 +1,156 @@
/*
* Copyright (c) 2007, Swedish Institute of Computer Science.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the Institute nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* This file is part of the Contiki operating system.
*
* $Id: profile.h,v 1.1 2007/10/23 20:39:07 adamdunkels Exp $
*/
/**
* \file
* Header file for the Contiki profiling system
* \author
* Adam Dunkels <adam@sics.se>
*/
#ifndef __PROFILE_H__
#define __PROFILE_H__
#include "sys/rtimer.h"
#ifdef EXPERIMENT_SETUP
#include "experiment-setup.h"
#endif
#include "contiki-conf.h"
#ifdef PROFILE_CONF_LIST_LENGTH
#define PROFILE_LIST_LENGTH PROFILE_CONF_LIST_LENGTH
#else
#define PROFILE_LIST_LENGTH 128
#endif
struct profile_timestamp {
const unsigned char *ptr;
rtimer_clock_t time;
};
extern struct profile_timestamp profile_timestamps[PROFILE_LIST_LENGTH];
extern unsigned int profile_timestamp_ptr;
extern rtimer_clock_t profile_timestamp_time;
#define PROFILE_TIMESTAMP_PTR (profile_timestamp_ptr / sizeof(struct profile_timestamp))
#if PROFILE_CONF_ON
#define PROFILE_TIMESTAMP(str) \
do { \
profile_timestamps[profile_timestamp_ptr / sizeof(struct profile_timestamp)].ptr = str; \
profile_timestamps[profile_timestamp_ptr / sizeof(struct profile_timestamp)].time = RTIMER_NOW(); \
profile_timestamp_ptr = (profile_timestamp_ptr + sizeof(struct profile_timestamp)) % (PROFILE_LIST_LENGTH * sizeof(struct profile_timestamp)); \
} while(0)
#define PROFILE_RESUME(num) PROFILE_TIMESTAMP(profile_timestamps[num].ptr)
#define PROFILE_COND_TIMESTAMP(cond, ptr) do { if(cond) {PROFILE_TIMESTAMP(ptr);} } while(0)
#define PROFILE_COND_RESUME(cond, num) PROFILE_COND_TIMESTAMP(cond, profile_timestamps[num].ptr)
#else /* PROFILE_CONF_ON */
#define PROFILE_TIMESTAMP(id)
#define PROFILE_RESUME(num)
#define PROFILE_COND_TIMESTAMP(cond, id)
#define PROFILE_COND_RESUME(cond, num)
#endif /* PROFILE_CONF_ON */
rtimer_clock_t profile_timediff(const char *ptr1, const char *ptr2);
#define PROFILE_GETPTR() (PROFILE_TIMESTAMP_PTR)
void profile_clear_timestamps(void);
void profile_init(void);
void profile_episode_start(void);
void profile_episode_end(void);
void profile_aggregates_print(void);
void profile_aggregates_compute(void);
void profile_print_stats(void);
enum {
PROFILE_TYPE_STACK,
PROFILE_TYPE_HW,
PROFILE_TYPE_RADIO,
PROFILE_TYPE_SYSTEM,
PROFILE_TYPE_APP,
};
#endif /* __PROFILE_H__ */
/* profile_timestamp_ptr code:
2e: 1f 42 00 00 mov &0x0000,r15 ;0x0000
32: 0e 4f mov r15, r14 ;
34: 0e 5e rla r14 ;
36: 0e 5e rla r14 ;
38: 3e 50 00 00 add #0, r14 ;#0x0000
3c: be 40 00 00 mov #0, 0(r14) ;#0x0000
40: 00 00
42: 9e 42 90 01 mov &0x0190,2(r14) ;0x0190
46: 02 00
48: 1f 53 inc r15 ;
4a: 3f f0 3f 00 and #63, r15 ;#0x003f
4e: 82 4f 00 00 mov r15, &0x0000 ;
msp430-specific profile_timetstamp_2ptr code:
2e: 1f 42 00 00 mov &0x0000,r15 ;0x0000
32: 0e 4f mov r15, r14 ;
34: 3e 50 00 00 add #0, r14 ;#0x0000
38: be 40 00 00 mov #0, 0(r14) ;#0x0000
3c: 00 00
3e: 9e 42 90 01 mov &0x0190,2(r14) ;0x0190
42: 02 00
44: 2f 53 incd r15 ;
46: 3f f0 7f 00 and #127, r15 ;#0x007f
4a: 82 4f 00 00 mov r15, &0x0000 ;
generic timestamp_2ptr code:
2e: 1f 42 00 00 mov &0x0000,r15 ;0x0000
32: 0e 4f mov r15, r14 ;
34: 1e c3 bic #1, r14 ;r3 As==01
36: 0e 5e rla r14 ;
38: 3e 50 00 00 add #0, r14 ;#0x0000
3c: be 40 00 00 mov #0, 0(r14) ;#0x0000
40: 00 00
42: 9e 42 90 01 mov &0x0190,2(r14) ;0x0190
46: 02 00
48: 2f 53 incd r15 ;
4a: 3f f0 7f 00 and #127, r15 ;#0x007f
4e: 82 4f 00 00 mov r15, &0x0000 ;
*/