proftool.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611
  1. /*
  2. * Copyright (c) 2013 Google, Inc
  3. *
  4. * This program is free software; you can redistribute it and/or
  5. * modify it under the terms of the GNU General Public License as
  6. * published by the Free Software Foundation; either version 2 of
  7. * the License, or (at your option) any later version.
  8. *
  9. * This program is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. * GNU General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU General Public License
  15. * along with this program; if not, write to the Free Software
  16. * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
  17. * MA 02111-1307 USA
  18. */
  19. /* Decode and dump U-Boot profiling information */
  20. #include <assert.h>
  21. #include <ctype.h>
  22. #include <limits.h>
  23. #include <regex.h>
  24. #include <stdarg.h>
  25. #include <stdio.h>
  26. #include <stdlib.h>
  27. #include <string.h>
  28. #include <unistd.h>
  29. #include <sys/param.h>
  30. #include <compiler.h>
  31. #include <trace.h>
  32. #define MAX_LINE_LEN 500
  33. enum {
  34. FUNCF_TRACE = 1 << 0, /* Include this function in trace */
  35. };
  36. struct func_info {
  37. unsigned long offset;
  38. const char *name;
  39. unsigned long code_size;
  40. unsigned long call_count;
  41. unsigned flags;
  42. /* the section this function is in */
  43. struct objsection_info *objsection;
  44. };
  45. enum trace_line_type {
  46. TRACE_LINE_INCLUDE,
  47. TRACE_LINE_EXCLUDE,
  48. };
  49. struct trace_configline_info {
  50. struct trace_configline_info *next;
  51. enum trace_line_type type;
  52. const char *name; /* identifier name / wildcard */
  53. regex_t regex; /* Regex to use if name starts with / */
  54. };
  55. /* The contents of the trace config file */
  56. struct trace_configline_info *trace_config_head;
  57. struct func_info *func_list;
  58. int func_count;
  59. struct trace_call *call_list;
  60. int call_count;
  61. int verbose; /* Verbosity level 0=none, 1=warn, 2=notice, 3=info, 4=debug */
  62. unsigned long text_offset; /* text address of first function */
  63. static void outf(int level, const char *fmt, ...)
  64. __attribute__ ((format (__printf__, 2, 3)));
  65. #define error(fmt, b...) outf(0, fmt, ##b)
  66. #define warn(fmt, b...) outf(1, fmt, ##b)
  67. #define notice(fmt, b...) outf(2, fmt, ##b)
  68. #define info(fmt, b...) outf(3, fmt, ##b)
  69. #define debug(fmt, b...) outf(4, fmt, ##b)
  70. static void outf(int level, const char *fmt, ...)
  71. {
  72. if (verbose >= level) {
  73. va_list args;
  74. va_start(args, fmt);
  75. vfprintf(stderr, fmt, args);
  76. va_end(args);
  77. }
  78. }
  79. static void usage(void)
  80. {
  81. fprintf(stderr,
  82. "Usage: proftool -cds -v3 <cmd> <profdata>\n"
  83. "\n"
  84. "Commands\n"
  85. " dump-ftrace\t\tDump out textual data in ftrace format\n"
  86. "\n"
  87. "Options:\n"
  88. " -m <map>\tSpecify Systen.map file\n"
  89. " -t <trace>\tSpecific trace data file (from U-Boot)\n"
  90. " -v <0-4>\tSpecify verbosity\n");
  91. exit(EXIT_FAILURE);
  92. }
  93. static int h_cmp_offset(const void *v1, const void *v2)
  94. {
  95. const struct func_info *f1 = v1, *f2 = v2;
  96. return (f1->offset / FUNC_SITE_SIZE) - (f2->offset / FUNC_SITE_SIZE);
  97. }
  98. static int read_system_map(FILE *fin)
  99. {
  100. unsigned long offset, start = 0;
  101. struct func_info *func;
  102. char buff[MAX_LINE_LEN];
  103. char symtype;
  104. char symname[MAX_LINE_LEN + 1];
  105. int linenum;
  106. int alloced;
  107. for (linenum = 1, alloced = func_count = 0;; linenum++) {
  108. int fields = 0;
  109. if (fgets(buff, sizeof(buff), fin))
  110. fields = sscanf(buff, "%lx %c %100s\n", &offset,
  111. &symtype, symname);
  112. if (fields == 2) {
  113. continue;
  114. } else if (feof(fin)) {
  115. break;
  116. } else if (fields < 2) {
  117. error("Map file line %d: invalid format\n", linenum);
  118. return 1;
  119. }
  120. /* Must be a text symbol */
  121. symtype = tolower(symtype);
  122. if (symtype != 't' && symtype != 'w')
  123. continue;
  124. if (func_count == alloced) {
  125. alloced += 256;
  126. func_list = realloc(func_list,
  127. sizeof(struct func_info) * alloced);
  128. assert(func_list);
  129. }
  130. if (!func_count)
  131. start = offset;
  132. func = &func_list[func_count++];
  133. memset(func, '\0', sizeof(*func));
  134. func->offset = offset - start;
  135. func->name = strdup(symname);
  136. func->flags = FUNCF_TRACE; /* trace by default */
  137. /* Update previous function's code size */
  138. if (func_count > 1)
  139. func[-1].code_size = func->offset - func[-1].offset;
  140. }
  141. notice("%d functions found in map file\n", func_count);
  142. text_offset = start;
  143. return 0;
  144. }
  145. static int read_data(FILE *fin, void *buff, int size)
  146. {
  147. int err;
  148. err = fread(buff, 1, size, fin);
  149. if (!err)
  150. return 1;
  151. if (err != size) {
  152. error("Cannot read profile file at pos %ld\n", ftell(fin));
  153. return -1;
  154. }
  155. return 0;
  156. }
  157. static struct func_info *find_func_by_offset(uint32_t offset)
  158. {
  159. struct func_info key, *found;
  160. key.offset = offset;
  161. found = bsearch(&key, func_list, func_count, sizeof(struct func_info),
  162. h_cmp_offset);
  163. return found;
  164. }
  165. /* This finds the function which contains the given offset */
  166. static struct func_info *find_caller_by_offset(uint32_t offset)
  167. {
  168. int low; /* least function that could be a match */
  169. int high; /* greated function that could be a match */
  170. struct func_info key;
  171. low = 0;
  172. high = func_count - 1;
  173. key.offset = offset;
  174. while (high > low + 1) {
  175. int mid = (low + high) / 2;
  176. int result;
  177. result = h_cmp_offset(&key, &func_list[mid]);
  178. if (result > 0)
  179. low = mid;
  180. else if (result < 0)
  181. high = mid;
  182. else
  183. return &func_list[mid];
  184. }
  185. return low >= 0 ? &func_list[low] : NULL;
  186. }
  187. static int read_calls(FILE *fin, int count)
  188. {
  189. struct trace_call *call_data;
  190. int i;
  191. notice("call count: %d\n", count);
  192. call_list = (struct trace_call *)calloc(count, sizeof(*call_data));
  193. if (!call_list) {
  194. error("Cannot allocate call_list\n");
  195. return -1;
  196. }
  197. call_count = count;
  198. call_data = call_list;
  199. for (i = 0; i < count; i++, call_data++) {
  200. if (read_data(fin, call_data, sizeof(*call_data)))
  201. return 1;
  202. }
  203. return 0;
  204. }
  205. static int read_profile(FILE *fin, int *not_found)
  206. {
  207. struct trace_output_hdr hdr;
  208. *not_found = 0;
  209. while (!feof(fin)) {
  210. int err;
  211. err = read_data(fin, &hdr, sizeof(hdr));
  212. if (err == 1)
  213. break; /* EOF */
  214. else if (err)
  215. return 1;
  216. switch (hdr.type) {
  217. case TRACE_CHUNK_FUNCS:
  218. /* Ignored at present */
  219. break;
  220. case TRACE_CHUNK_CALLS:
  221. if (read_calls(fin, hdr.rec_count))
  222. return 1;
  223. break;
  224. }
  225. }
  226. return 0;
  227. }
  228. static int read_map_file(const char *fname)
  229. {
  230. FILE *fmap;
  231. int err = 0;
  232. fmap = fopen(fname, "r");
  233. if (!fmap) {
  234. error("Cannot open map file '%s'\n", fname);
  235. return 1;
  236. }
  237. if (fmap) {
  238. err = read_system_map(fmap);
  239. fclose(fmap);
  240. }
  241. return err;
  242. }
  243. static int read_profile_file(const char *fname)
  244. {
  245. int not_found = INT_MAX;
  246. FILE *fprof;
  247. int err;
  248. fprof = fopen(fname, "rb");
  249. if (!fprof) {
  250. error("Cannot open profile data file '%s'\n",
  251. fname);
  252. return 1;
  253. } else {
  254. err = read_profile(fprof, &not_found);
  255. fclose(fprof);
  256. if (err)
  257. return err;
  258. if (not_found) {
  259. warn("%d profile functions could not be found in the map file - are you sure that your profile data and map file correspond?\n",
  260. not_found);
  261. return 1;
  262. }
  263. }
  264. return 0;
  265. }
  266. static int regex_report_error(regex_t *regex, int err, const char *op,
  267. const char *name)
  268. {
  269. char buf[200];
  270. regerror(err, regex, buf, sizeof(buf));
  271. error("Regex error '%s' in %s '%s'\n", buf, op, name);
  272. return -1;
  273. }
  274. static void check_trace_config_line(struct trace_configline_info *item)
  275. {
  276. struct func_info *func, *end;
  277. int err;
  278. debug("Checking trace config line '%s'\n", item->name);
  279. for (func = func_list, end = func + func_count; func < end; func++) {
  280. err = regexec(&item->regex, func->name, 0, NULL, 0);
  281. debug(" - regex '%s', string '%s': %d\n", item->name,
  282. func->name, err);
  283. if (err == REG_NOMATCH)
  284. continue;
  285. if (err) {
  286. regex_report_error(&item->regex, err, "match",
  287. item->name);
  288. break;
  289. }
  290. /* It matches, so perform the action */
  291. switch (item->type) {
  292. case TRACE_LINE_INCLUDE:
  293. info(" include %s at %lx\n", func->name,
  294. text_offset + func->offset);
  295. func->flags |= FUNCF_TRACE;
  296. break;
  297. case TRACE_LINE_EXCLUDE:
  298. info(" exclude %s at %lx\n", func->name,
  299. text_offset + func->offset);
  300. func->flags &= ~FUNCF_TRACE;
  301. break;
  302. }
  303. }
  304. }
  305. static void check_trace_config(void)
  306. {
  307. struct trace_configline_info *line;
  308. for (line = trace_config_head; line; line = line->next)
  309. check_trace_config_line(line);
  310. }
  311. /**
  312. * Check the functions to see if they each have an objsection. If not, then
  313. * the linker must have eliminated them.
  314. */
  315. static void check_functions(void)
  316. {
  317. struct func_info *func, *end;
  318. unsigned long removed_code_size = 0;
  319. int not_found = 0;
  320. /* Look for missing functions */
  321. for (func = func_list, end = func + func_count; func < end; func++) {
  322. if (!func->objsection) {
  323. removed_code_size += func->code_size;
  324. not_found++;
  325. }
  326. }
  327. /* Figure out what functions we want to trace */
  328. check_trace_config();
  329. warn("%d functions removed by linker, %ld code size\n",
  330. not_found, removed_code_size);
  331. }
  332. static int read_trace_config(FILE *fin)
  333. {
  334. char buff[200];
  335. int linenum = 0;
  336. struct trace_configline_info **tailp = &trace_config_head;
  337. while (fgets(buff, sizeof(buff), fin)) {
  338. int len = strlen(buff);
  339. struct trace_configline_info *line;
  340. char *saveptr;
  341. char *s, *tok;
  342. int err;
  343. linenum++;
  344. if (len && buff[len - 1] == '\n')
  345. buff[len - 1] = '\0';
  346. /* skip blank lines and comments */
  347. for (s = buff; *s == ' ' || *s == '\t'; s++)
  348. ;
  349. if (!*s || *s == '#')
  350. continue;
  351. line = (struct trace_configline_info *)calloc(1,
  352. sizeof(*line));
  353. if (!line) {
  354. error("Cannot allocate config line\n");
  355. return -1;
  356. }
  357. tok = strtok_r(s, " \t", &saveptr);
  358. if (!tok) {
  359. error("Invalid trace config data on line %d\n",
  360. linenum);
  361. return -1;
  362. }
  363. if (0 == strcmp(tok, "include-func")) {
  364. line->type = TRACE_LINE_INCLUDE;
  365. } else if (0 == strcmp(tok, "exclude-func")) {
  366. line->type = TRACE_LINE_EXCLUDE;
  367. } else {
  368. error("Unknown command in trace config data line %d\n",
  369. linenum);
  370. return -1;
  371. }
  372. tok = strtok_r(NULL, " \t", &saveptr);
  373. if (!tok) {
  374. error("Missing pattern in trace config data line %d\n",
  375. linenum);
  376. return -1;
  377. }
  378. err = regcomp(&line->regex, tok, REG_NOSUB);
  379. if (err) {
  380. free(line);
  381. return regex_report_error(&line->regex, err, "compile",
  382. tok);
  383. }
  384. /* link this new one to the end of the list */
  385. line->name = strdup(tok);
  386. line->next = NULL;
  387. *tailp = line;
  388. tailp = &line->next;
  389. }
  390. if (!feof(fin)) {
  391. error("Cannot read from trace config file at position %ld\n",
  392. ftell(fin));
  393. return -1;
  394. }
  395. return 0;
  396. }
  397. static int read_trace_config_file(const char *fname)
  398. {
  399. FILE *fin;
  400. int err;
  401. fin = fopen(fname, "r");
  402. if (!fin) {
  403. error("Cannot open trace_config file '%s'\n", fname);
  404. return -1;
  405. }
  406. err = read_trace_config(fin);
  407. fclose(fin);
  408. return err;
  409. }
  410. static void out_func(ulong func_offset, int is_caller, const char *suffix)
  411. {
  412. struct func_info *func;
  413. func = (is_caller ? find_caller_by_offset : find_func_by_offset)
  414. (func_offset);
  415. if (func)
  416. printf("%s%s", func->name, suffix);
  417. else
  418. printf("%lx%s", func_offset, suffix);
  419. }
  420. /*
  421. * # tracer: function
  422. * #
  423. * # TASK-PID CPU# TIMESTAMP FUNCTION
  424. * # | | | | |
  425. * # bash-4251 [01] 10152.583854: path_put <-path_walk
  426. * # bash-4251 [01] 10152.583855: dput <-path_put
  427. * # bash-4251 [01] 10152.583855: _atomic_dec_and_lock <-dput
  428. */
  429. static int make_ftrace(void)
  430. {
  431. struct trace_call *call;
  432. int missing_count = 0, skip_count = 0;
  433. int i;
  434. printf("# tracer: ftrace\n"
  435. "#\n"
  436. "# TASK-PID CPU# TIMESTAMP FUNCTION\n"
  437. "# | | | | |\n");
  438. for (i = 0, call = call_list; i < call_count; i++, call++) {
  439. struct func_info *func = find_func_by_offset(call->func);
  440. ulong time = call->flags & FUNCF_TIMESTAMP_MASK;
  441. if (TRACE_CALL_TYPE(call) != FUNCF_ENTRY &&
  442. TRACE_CALL_TYPE(call) != FUNCF_EXIT)
  443. continue;
  444. if (!func) {
  445. warn("Cannot find function at %lx\n",
  446. text_offset + call->func);
  447. missing_count++;
  448. continue;
  449. }
  450. if (!(func->flags & FUNCF_TRACE)) {
  451. debug("Funcion '%s' is excluded from trace\n",
  452. func->name);
  453. skip_count++;
  454. continue;
  455. }
  456. printf("%16s-%-5d [01] %lu.%06lu: ", "uboot", 1,
  457. time / 1000000, time % 1000000);
  458. out_func(call->func, 0, " <- ");
  459. out_func(call->caller, 1, "\n");
  460. }
  461. info("ftrace: %d functions not found, %d excluded\n", missing_count,
  462. skip_count);
  463. return 0;
  464. }
  465. static int prof_tool(int argc, char * const argv[],
  466. const char *prof_fname, const char *map_fname,
  467. const char *trace_config_fname)
  468. {
  469. int err = 0;
  470. if (read_map_file(map_fname))
  471. return -1;
  472. if (prof_fname && read_profile_file(prof_fname))
  473. return -1;
  474. if (trace_config_fname && read_trace_config_file(trace_config_fname))
  475. return -1;
  476. check_functions();
  477. for (; argc; argc--, argv++) {
  478. const char *cmd = *argv;
  479. if (0 == strcmp(cmd, "dump-ftrace"))
  480. err = make_ftrace();
  481. else
  482. warn("Unknown command '%s'\n", cmd);
  483. }
  484. return err;
  485. }
  486. int main(int argc, char *argv[])
  487. {
  488. const char *map_fname = "System.map";
  489. const char *prof_fname = NULL;
  490. const char *trace_config_fname = NULL;
  491. int opt;
  492. verbose = 2;
  493. while ((opt = getopt(argc, argv, "m:p:t:v:")) != -1) {
  494. switch (opt) {
  495. case 'm':
  496. map_fname = optarg;
  497. break;
  498. case 'p':
  499. prof_fname = optarg;
  500. break;
  501. case 't':
  502. trace_config_fname = optarg;
  503. break;
  504. case 'v':
  505. verbose = atoi(optarg);
  506. break;
  507. default:
  508. usage();
  509. }
  510. }
  511. argc -= optind; argv += optind;
  512. if (argc < 1)
  513. usage();
  514. debug("Debug enabled\n");
  515. return prof_tool(argc, argv, prof_fname, map_fname,
  516. trace_config_fname);
  517. }