/* * Original by Ramon Garcia Fernandez * Hacked by linus * then mingo broke it all * hacked more by cel@netapp.com */ #include #include #include #include #include #define prof_func "sys_open" #define BUFSIZE 1024 struct entry { struct entry * next; long long time; /* * With APIC irqs we can get more than * 4G profiling hits. */ unsigned long address; char type; /* * We profile only functions currently, * so we can do some sanity checking by analyzing * the symbol type field in System.map */ char name[1]; }; struct entry * list = NULL; void do_symbol(long long time, unsigned long address, char * name, char type) { struct entry * entry = malloc(sizeof(struct entry) + strlen(name)); struct entry ** tmp; entry->time = time; entry->address = address; strcpy(entry->name, name); entry->type = type; tmp = &list; while (*tmp) { if ((*tmp)->time > entry->time) break; tmp = &(*tmp)->next; } entry->next = *tmp; *tmp = entry; } void show_symbols(long long total) { int had_type_conflict=0; struct entry * entry = list; long long sanity_total=0; printf("\n//\n// Function granularity sorted histogram:\n"); printf("//---------------------------------------\n"); while (entry) { printf("%12Ld %5Ld.%02Ld%% %08lx %s\n" , entry->time, (entry->time*10000/total) / 100, (entry->time*10000/total) % 100, entry->address, entry->name); if ((entry->type != 't') && (entry->type != 'T')) { had_type_conflict=1; printf("// TYPE CONFLICT (type:'%c', symbol:'%s').\n", entry->type,entry->name); } sanity_total += entry->time; entry = entry->next; } printf("//-----------------------------\n"); printf("%12Ld 100.00%% 00000000 TOTAL\n" ,total); if (total != sanity_total) printf("WARNING: sum of times(%Ld) != total.\n", sanity_total); if (had_type_conflict) { fprintf(stderr, "\n// WARNING: type conflict detected."); fprintf(stderr, " (wrong System.map?)\n"); exit(1); } } inline void show_finegrained_function (int has_read, int * buffer, int step, char * func, int addr) { long long sum=0; int i; for ( i = 0 ; i < has_read/sizeof(int) ; i++) sum += buffer[i]; if (!sum) printf("// (no profiling hits in %s()).\n", func); else { printf("// Fine grained profile of %s().\n", func); printf("//\n"); printf("// TOTAL: 100.000000%% %12Ld\n", sum); printf("//-----------------------------------\n"); for ( i = 0 ; i < has_read/sizeof(int) ; i++) { /* * Could someone please enhance/fix printf's %f * conversion type? This workaround to get proper * padding and position is _soooo_ silly. */ printf(" %08x %3Ld.%06Ld%% %12d\n", i*step + addr, ((long long)buffer[i]*100)/sum, (((long long)buffer[i]*100)%sum)*1000000/sum, buffer[i] ); } } } int main(int argc, char ** argv) { int profile_data; char * func = prof_func; FILE *kmap; int current_symbol_value , next_symbol_value, code_offset; char current_symbol[80] , next_symbol[80]; int has_read , j; long long total = 0; off_t profile_size; unsigned long end_address=0; unsigned int step; /* * We can read the profiling step from * /proc/profile directly, so we are not * compilation dependent */ char type, next_type; int found_function=0, just_found_function=0; /* * This is needed for ultra-low * resolution profiling. */ if (argc == 2) func = argv[1]; profile_data = open("/proc/profile", O_RDONLY); if (profile_data < 0) { perror("/proc/profile"); exit(1); } kmap = fopen("/boot/System.map","r"); if (!kmap) { kmap = fopen("/usr/src/linux/System.map","r"); if (!kmap) { perror("System.map"); exit(1); } } /* * The size of /proc/profile is a very good sanity check, it should end * with _etext. If not the System.map is bolixed. * * The size of /proc/profile is (prof_len + 1) * sizeof(unsigned int) * where prof_len is the number of buckets in the histogram. * (from fs/proc/proc_misc.c) */ { struct stat statbuf; fstat(profile_data, &statbuf); profile_size = statbuf.st_size; } fscanf(kmap, "%x %*s %s\n", ¤t_symbol_value, current_symbol); fscanf(kmap, "%x %c %s\n", &next_symbol_value, &next_type, next_symbol); /* * We expect the _text symbol at the top of the System.map. */ if (strcmp(current_symbol,"_text")) { fprintf(stderr, "expecting _text as first System.map symbol.\n%s", "(maybe stray undefined symbols in System.map?).\n"); close(profile_data); fclose(kmap); exit(1); } /* * Here we read the profiling step from /proc/profile. This is a good * sanity check as well. Nothing should go wrong after this. */ has_read = read(profile_data, &step , sizeof(step) ); if (has_read != sizeof(step)) { perror("Couldnt read step from /proc/profile."); close(profile_data); fclose(kmap); exit(1); } printf("// Step: %d, Profiling %s().\n\n",step, func); /* * Main read-analyze loop. */ for (;;) { unsigned long long tiempo = 0; unsigned int buffer [(next_symbol_value - current_symbol_value)/step]; if (!strcmp(func, current_symbol)) { found_function=1; just_found_function=1; } if (!strcmp("_stext", current_symbol)) code_offset = current_symbol_value; if (!strcmp("_etext", current_symbol)) end_address = current_symbol_value; if ((next_symbol_value / step) == (current_symbol_value / step)) { strcpy(current_symbol, next_symbol); type = next_type; fscanf(kmap, "%x %c %s\n", &next_symbol_value, &next_type, next_symbol); continue; } lseek(profile_data, (sizeof(int)+ (current_symbol_value - code_offset) / step * sizeof(int)), SEEK_SET); has_read = read(profile_data, buffer , sizeof(buffer) ); if (just_found_function) { just_found_function=0; show_finegrained_function(has_read, buffer, step, current_symbol, current_symbol_value); } for ( j = 0 ; j < has_read/sizeof(int) ; j++) tiempo += buffer[j]; if (tiempo != 0) { do_symbol(tiempo, current_symbol_value, current_symbol, type); total += tiempo; } if ((has_read < (next_symbol_value-current_symbol_value)/ step*sizeof(int)) || next_symbol_value == current_symbol_value ) break; strcpy ( current_symbol , next_symbol ); current_symbol_value = next_symbol_value; type = next_type; fscanf(kmap , "%x %c %s\n" , &next_symbol_value , &next_type, next_symbol ); } close(profile_data); fclose(kmap); show_symbols(total); if ( abs((int)(profile_size-sizeof(int))/sizeof(int)*step -(end_address-code_offset)) > (int)step) { fprintf(stderr,"\n// WARNING: wrong _etext symbol.\n"); fprintf(stderr,"// (wrong System.map?)\n"); exit(1); } if (!found_function) { fprintf(stderr,"\n// WARNING: Symbol '%s' not in System.map.\n", func); exit(1); } return(0); }