Compare commits
12 Commits
1635c965fe
...
b61b35b812
Author | SHA1 | Date |
---|---|---|
|
b61b35b812 | |
|
8d5d145d0f | |
|
1d9318be0d | |
|
7c09e3de9c | |
|
91553233ca | |
|
7a99e5c671 | |
|
068862dc1e | |
|
3752a970ef | |
|
d3bad18e33 | |
|
7b6ef3e2b0 | |
|
5083823b20 | |
|
845519aeea |
|
@ -11,7 +11,6 @@ module rvprof
|
|||
public :: rvprof_finalize
|
||||
public :: rvprof_region_begin
|
||||
public :: rvprof_region_end
|
||||
public :: rvprof_set_cpu_frequency
|
||||
public :: rvprof_set_program_name
|
||||
|
||||
interface
|
||||
|
|
|
@ -71,6 +71,8 @@ void rvprof_init(const char* output_file){
|
|||
|
||||
// register atexit handler to automate cleanup
|
||||
register_atexit_handler();
|
||||
|
||||
fprintf(stderr, "rvprof: profiling active.");
|
||||
}
|
||||
|
||||
void rvprof_set_program_name(const char* program_name){
|
||||
|
@ -107,7 +109,7 @@ void rvprof_region_begin(const char* name){
|
|||
region->child_time = 0;
|
||||
region->child_cycles = 0;
|
||||
region->func_addr = NULL;
|
||||
region->stack_id = rvprof_stats_get_or_create_stack_id;
|
||||
region->stack_id = rvprof_stats_get_or_create_stack_id();
|
||||
|
||||
// look up or create ID for region
|
||||
const char* caller;
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
#include "rvprof_internal.h"
|
||||
|
||||
// Here, we override cygnus function hooks to insert our own profiling logic for all
|
||||
// code parts compiled using -finstrument functions.
|
||||
|
||||
void __cyg_profile_func_enter(void *this_fn, void *call_site){
|
||||
|
||||
if (!g_rvprof.config.enable_hooks) return;
|
||||
|
||||
const char* func_name = rvprof_symbols_lookup(this_fn);
|
||||
rvprof_region_begin(func_name);
|
||||
|
||||
|
||||
}
|
||||
|
||||
void __cyg_profile_func_exit(void *this_fn, void *call_site){
|
||||
if (!g_rvprof.config.enable_hooks || !g_rvprof.initialized || g_rvprof.stack_ptr < 0) return;
|
||||
|
||||
const char* func_name = rvprof_symbols_lookup(this_fn);
|
||||
rvprof_region_end(func_name);
|
||||
|
||||
// auto-finalize on main function exit
|
||||
if (rvprof_utils_is_main_function(func_name)) {
|
||||
fprintf(stderr, "RVProf: Auto-finalizing on main() exit\n");
|
||||
rvprof_finalize();
|
||||
}
|
||||
}
|
|
@ -36,7 +36,7 @@ typedef enum {
|
|||
// symbol table entry
|
||||
typedef struct{
|
||||
uintptr_t addr;
|
||||
char name[MAX_NAME_LEN];
|
||||
char* name;
|
||||
size_t size;
|
||||
} symbol_entry_t;
|
||||
|
||||
|
@ -133,9 +133,9 @@ extern rvprof_context_t g_rvprof;
|
|||
|
||||
// internal API
|
||||
|
||||
// memory management
|
||||
// memory management, used to track memory overhead
|
||||
void* rvprof_malloc(size_t size);
|
||||
void* rvprof_reallovc(void* ptr, size_t old_size, size_t new_size);
|
||||
void* rvprof_realloc(void* ptr, size_t old_size, size_t new_size);
|
||||
void rvprof_free(void* ptr, size_t size);
|
||||
|
||||
// dynamic array management
|
||||
|
@ -174,6 +174,18 @@ void rvprof_stats_add_caller_to_function(int func_id, const char* caller);
|
|||
// output
|
||||
rvprof_error_t rvprof_output_generate_report(void);
|
||||
|
||||
// utilities
|
||||
char* rvprof_utils_get_program_name(void);
|
||||
char* rvprof_utils_generate_output_filename(void);
|
||||
int rvprof_utils_is_main_function(const char* func_name);
|
||||
|
||||
// function stats
|
||||
int rvprof_stats_find_or_create_function(const char* name, const char* caller);
|
||||
int rvprof_stats_get_or_create_stack_id(void);
|
||||
void rvprof_stats_add_stack_id_to_function(int func_id, int stack_id);
|
||||
void rvprof_stats_add_caller_to_function(int func_id, const char* caller);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,116 @@
|
|||
#include "rvprof_internal.h"
|
||||
|
||||
// custom malloc, realloc and free help us tracking the memory footprint of rvprof
|
||||
|
||||
void* rvprof_malloc(size_t size){
|
||||
void* ptr = malloc(size);
|
||||
if (ptr) g_rvprof.total_memory_allocated += size;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void* rvprof_realloc(void* ptr, size_t old_size, size_t new_size){
|
||||
void* new_ptr = realloc(ptr, new_size);
|
||||
if (new_ptr) g_rvprof.total_memory_allocated += (new_size - old_size);
|
||||
return new_ptr;
|
||||
}
|
||||
|
||||
void rvprof_free(void* ptr, size_t size){
|
||||
if(ptr){
|
||||
free(ptr);
|
||||
g_rvprof.total_memory_allocated -= size;
|
||||
}
|
||||
}
|
||||
|
||||
// DYNAMIC ARRAY MANAGEMENT
|
||||
|
||||
|
||||
// generic ensure_capacity implementation, independent of actual array type
|
||||
// maybe if switching to C++ in the future, we may move from pre-processor magic to templates
|
||||
#define IMPLEMENT_ARRAY_ENSURE_CAPACITY(type, name, initial_size) \
|
||||
int name##_array_ensure_capacity(name##_array_t* arr, int needed){ \
|
||||
if (needed >= arr-> capacity){ \
|
||||
int new_capacity; \
|
||||
if (arr->capacity == 0){ \
|
||||
new_capacity = initial_size; \
|
||||
} else { \
|
||||
new_capacity = arr->capacity; \
|
||||
} \
|
||||
\
|
||||
while (new_capacity <= needed){ \
|
||||
new_capacity *= GROWTH_FACTOR; \
|
||||
} \
|
||||
\
|
||||
size_t old_size = arr->capacity * sizeof(type); \
|
||||
size_t new_size = new_capacity * sizeof(type); \
|
||||
type* new_data = rvprof_realloc(arr->data, old_size, new_size); \
|
||||
if(!new_data){ \
|
||||
return RVPROF_ERROR_MEMORY; \
|
||||
} \
|
||||
\
|
||||
arr->data = new_data; \
|
||||
arr->capacity = new_capacity; \
|
||||
} \
|
||||
return RVPROF_SUCCESS;\
|
||||
}
|
||||
|
||||
// do the same for cleanup, taking a fuction alias for the slightly different cleanup logic for each type
|
||||
#define IMPLEMENT_ARRAY_CLEANUP(type, name, cleanup_fn) \
|
||||
void name##_array_cleanup(name##_array_t* arr){ \
|
||||
if (arr->data){ \
|
||||
for(int i=0; i<arr->size; i++){ \
|
||||
cleanup_fn(&arr->data[i]); \
|
||||
} \
|
||||
rvprof_free(arr->data, arr->capacity * sizeof(type)); \
|
||||
arr->data = NULL; \
|
||||
arr->size = 0; \
|
||||
arr->capacity = 0; \
|
||||
} \
|
||||
}
|
||||
|
||||
// full array implementation macro
|
||||
#define IMPLEMENT_DYNAMIC_ARRAY(type, name, initial_size, cleanup_fn) \
|
||||
IMPLEMENT_ARRAY_ENSURE_CAPACITY(type, name, initial_size) \
|
||||
IMPLEMENT_ARRAY_CLEANUP(type, name, cleanup_fn)
|
||||
|
||||
// type-specific cleanups
|
||||
static void cleanup_region_entry(void* item){
|
||||
// region array doesn't need nested cleanup
|
||||
return;
|
||||
}
|
||||
|
||||
static void cleanup_symbol_entry(void* item){
|
||||
symbol_entry_t* symbol = (symbol_entry_t*)item;
|
||||
rvprof_free(symbol->name, strlen(symbol->name)+1);
|
||||
symbol->name = NULL;
|
||||
}
|
||||
|
||||
static void cleanup_function_stats(void* item){
|
||||
function_stats_t* stats = (function_stats_t*)item;
|
||||
if (stats->stack_ids) {
|
||||
rvprof_free(stats->stack_ids, stats->max_stack_ids*sizeof(int));
|
||||
stats->stack_ids = NULL;
|
||||
}
|
||||
if (stats->callers) {
|
||||
for (int i=0; i<stats->num_callers; i++){
|
||||
rvprof_free(stats->callers[i], strlen(stats->callers[i]+1));
|
||||
}
|
||||
rvprof_free(stats->callers, stats->max_callers*sizeof(char*));
|
||||
stats->callers = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void cleanup_stack_info(void* item){
|
||||
stack_info_t* stack = (stack_info_t*)item;
|
||||
if (stack->stack_path){
|
||||
rvprof_free(stack->stack_path, strlen(stack->stack_path)+1);
|
||||
stack->stack_path = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// acutal dynamic array implementations:
|
||||
IMPLEMENT_DYNAMIC_ARRAY(region_t, region, INITIAL_REGIONS, cleanup_region_entry)
|
||||
IMPLEMENT_DYNAMIC_ARRAY(symbol_entry_t, symbol, INITIAL_SYMBOLS, cleanup_symbol_entry)
|
||||
IMPLEMENT_DYNAMIC_ARRAY(function_stats_t, function_stats, INITIAL_FUNCTIONS, cleanup_function_stats)
|
||||
IMPLEMENT_DYNAMIC_ARRAY(stack_info_t, stack_info, INITIAL_STACKS, cleanup_stack_info)
|
||||
|
||||
|
Loading…
Reference in New Issue