gprof.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527
  1. /*-
  2. * Copyright (c) 1983, 1992, 1993
  3. * The Regents of the University of California. 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. * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. // Modified based on
  30. // - https://github.com/bminor/newlib/blob/master/winsup/cygwin/gmon.c
  31. // - https://github.com/bminor/newlib/blob/master/winsup/cygwin/local_includes/gmon.h
  32. #include <fcntl.h>
  33. #include <stdlib.h>
  34. #include <stdio.h>
  35. #include <unistd.h>
  36. #include <stdint.h>
  37. #include <string.h>
  38. #include "gprof_api.h"
  39. //#define DEBUG
  40. /*
  41. * Structure prepended to gmon.out profiling data file.
  42. */
  43. struct gmonhdr {
  44. size_t lpc; /* base pc address of sample buffer */
  45. size_t hpc; /* max pc address of sampled buffer */
  46. int ncnt; /* size of sample buffer (plus this header) */
  47. int version; /* version number */
  48. int profrate; /* profiling clock rate */
  49. int spare[3]; /* reserved */
  50. };
  51. #define GMONVERSION 0x00051879
  52. /*
  53. * histogram counters are unsigned shorts (according to the kernel).
  54. */
  55. #define HISTCOUNTER unsigned short
  56. /*
  57. * fraction of text space to allocate for histogram counters here, 1/2
  58. */
  59. #define HISTFRACTION 2
  60. /*
  61. * Fraction of text space to allocate for from hash buckets.
  62. * The value of HASHFRACTION is based on the minimum number of bytes
  63. * of separation between two subroutine call points in the object code.
  64. * Given MIN_SUBR_SEPARATION bytes of separation the value of
  65. * HASHFRACTION is calculated as:
  66. *
  67. * HASHFRACTION = MIN_SUBR_SEPARATION / (2 * sizeof(short) - 1);
  68. *
  69. * For example, on the VAX, the shortest two call sequence is:
  70. *
  71. * calls $0,(r0)
  72. * calls $0,(r0)
  73. *
  74. * which is separated by only three bytes, thus HASHFRACTION is
  75. * calculated as:
  76. *
  77. * HASHFRACTION = 3 / (2 * 2 - 1) = 1
  78. *
  79. * Note that the division above rounds down, thus if MIN_SUBR_FRACTION
  80. * is less than three, this algorithm will not work!
  81. *
  82. * In practice, however, call instructions are rarely at a minimal
  83. * distance. Hence, we will define HASHFRACTION to be 2 across all
  84. * architectures. This saves a reasonable amount of space for
  85. * profiling data structures without (in practice) sacrificing
  86. * any granularity.
  87. */
  88. #define HASHFRACTION 2
  89. /*
  90. * percent of text space to allocate for tostructs with a minimum.
  91. */
  92. #define ARCDENSITY 2 /* this is in percentage, relative to text size! */
  93. #define MINARCS 50
  94. #define MAXARCS ((1 << (8 * sizeof(HISTCOUNTER))) - 2)
  95. struct tostruct {
  96. size_t selfpc; /* callee address/program counter. The caller address is in froms[] array which points to tos[] array */
  97. long count; /* how many times it has been called */
  98. unsigned short link; /* link to next entry in hash table. For tos[0] this points to the last used entry */
  99. unsigned short pad; /* additional padding bytes, to have entries 4byte aligned */
  100. };
  101. /*
  102. * a raw arc, with pointers to the calling site and
  103. * the called site and a count.
  104. */
  105. struct rawarc {
  106. size_t raw_frompc;
  107. size_t raw_selfpc;
  108. long raw_count;
  109. };
  110. /*
  111. * general rounding functions.
  112. */
  113. #define ROUNDDOWN(x,y) (((x)/(y))*(y))
  114. #define ROUNDUP(x,y) ((((x)+(y)-1)/(y))*(y))
  115. /*
  116. * The profiling data structures are housed in this structure.
  117. */
  118. struct gmonparam {
  119. int state;
  120. int already_setup;
  121. unsigned short *kcount; /* histogram PC sample array */
  122. size_t kcountsize; /* size of kcount[] array in bytes */
  123. unsigned short *froms; /* array of hashed 'from' addresses. The 16bit value is an index into the tos[] array */
  124. size_t fromssize; /* size of froms[] array in bytes */
  125. struct tostruct *tos; /* to struct, contains histogram counter */
  126. size_t tossize; /* size of tos[] array in bytes */
  127. long tolimit;
  128. size_t lowpc; /* low program counter of area */
  129. size_t highpc; /* high program counter */
  130. size_t textsize; /* code size */
  131. size_t scale;
  132. };
  133. /* gprof data structure */
  134. struct gprofdata {
  135. char *buf;
  136. uint32_t size;
  137. };
  138. /* convert an addr to an index */
  139. #define PROFIDX(pc, base, scale) \
  140. ({ \
  141. size_t i = (pc - base) / 2; \
  142. if (sizeof (unsigned long long int) > sizeof (size_t)) { \
  143. i = (unsigned long long int) i * scale / 65536; \
  144. } else { \
  145. i = i / 65536 * scale + i % 65536 * scale / 65536; \
  146. } \
  147. i; \
  148. })
  149. /* convert an index into an address */
  150. #define PROFADDR(idx, base, scale) \
  151. ((base) + ((((unsigned long long)(idx) << 16) \
  152. / (unsigned long long)(scale)) << 1))
  153. /*
  154. * Possible states of profiling.
  155. */
  156. #define GMON_PROF_ON 0
  157. #define GMON_PROF_BUSY 1
  158. #define GMON_PROF_ERROR 2
  159. #define GMON_PROF_OFF 3
  160. #define ERR(s) write(STDERR_FILENO, s, sizeof(s))
  161. static struct gmonparam _gmonparam = { GMON_PROF_OFF, 0, NULL,
  162. 0, NULL, 0, NULL, 0, 0L, 0, 0, 0, 0 };
  163. /* see profil(2) where this is described (incorrectly) */
  164. #define SCALE_1_TO_1 0x10000L
  165. #define GMONPARAM (&_gmonparam)
  166. /* Where the gprof data stored after execute gprof_collect(0) */
  167. struct gprofdata gprof_data = {NULL, 0};
  168. /*
  169. * Control profiling
  170. * profiling is what mcount checks to see if
  171. * all the data structures are ready.
  172. */
  173. static void moncontrol(int mode)
  174. {
  175. struct gmonparam *p = GMONPARAM;
  176. if (mode) {
  177. /* start */
  178. gprof_on();
  179. p->state = GMON_PROF_ON;
  180. } else {
  181. /* stop */
  182. gprof_off();
  183. p->state = GMON_PROF_OFF;
  184. }
  185. }
  186. static void monstartup(size_t lowpc, size_t highpc)
  187. {
  188. register size_t o;
  189. char *cp;
  190. struct gmonparam *p = GMONPARAM;
  191. /*
  192. * round lowpc and highpc to multiples of the density we're using
  193. * so the rest of the scaling (here and in gprof) stays in ints.
  194. */
  195. p->lowpc = ROUNDDOWN(lowpc, HISTFRACTION * sizeof(HISTCOUNTER));
  196. p->highpc = ROUNDUP(highpc, HISTFRACTION * sizeof(HISTCOUNTER));
  197. p->textsize = p->highpc - p->lowpc;
  198. p->kcountsize = p->textsize / HISTFRACTION;
  199. p->fromssize = p->textsize / HASHFRACTION;
  200. p->tolimit = p->textsize * ARCDENSITY / 100;
  201. if (p->tolimit < MINARCS) {
  202. p->tolimit = MINARCS;
  203. } else if (p->tolimit > MAXARCS) {
  204. p->tolimit = MAXARCS;
  205. }
  206. p->tossize = p->tolimit * sizeof(struct tostruct);
  207. cp = malloc(p->kcountsize + p->fromssize + p->tossize);
  208. if (cp == (char*) NULL) {
  209. p->state = GMON_PROF_ERROR;
  210. ERR("monstartup: out of memory\n");
  211. return;
  212. }
  213. /* zero out cp as value will be added there */
  214. memset(cp, 0, p->kcountsize + p->fromssize + p->tossize);
  215. p->tos = (struct tostruct*) cp;
  216. cp += p->tossize;
  217. p->kcount = (unsigned short*) cp;
  218. cp += p->kcountsize;
  219. p->froms = (unsigned short*) cp;
  220. p->tos[0].link = 0;
  221. o = p->highpc - p->lowpc;
  222. if (p->kcountsize < o) {
  223. #ifndef notdef
  224. p->scale = ((float) p->kcountsize / o) * SCALE_1_TO_1;
  225. #else /* avoid floating point */
  226. int quot = o / p->kcountsize;
  227. if (quot >= 0x10000)
  228. p->scale = 1;
  229. else if (quot >= 0x100)
  230. p->scale = 0x10000 / quot;
  231. else if (o >= 0x800000)
  232. p->scale = 0x1000000 / (o / (p->kcountsize >> 8));
  233. else
  234. p->scale = 0x1000000 / ((o << 8) / p->kcountsize);
  235. #endif
  236. } else {
  237. p->scale = SCALE_1_TO_1;
  238. }
  239. moncontrol(1); /* start */
  240. }
  241. void _mcount(uint32_t *frompcindex)
  242. {
  243. register uint32_t *selfpc asm("ra");
  244. register struct tostruct *top;
  245. register struct tostruct *prevtop;
  246. register long toindex;
  247. struct gmonparam *p = GMONPARAM;
  248. if (!p->already_setup) {
  249. p->already_setup = 1;
  250. monstartup((size_t) PROGRAM_LOWPC, (size_t) PROGRAM_HIGHPC);
  251. }
  252. /*
  253. * check that we are profiling
  254. * and that we aren't recursively invoked.
  255. */
  256. if (p->state != GMON_PROF_ON) {
  257. goto out;
  258. }
  259. p->state++;
  260. /*
  261. * check that frompcindex is a reasonable pc value.
  262. * for example: signal catchers get called from the stack,
  263. * not from text space. too bad.
  264. */
  265. frompcindex = (uint32_t*) ((unsigned long) frompcindex - (unsigned long) p->lowpc);
  266. if ((unsigned long) frompcindex > p->textsize) {
  267. goto done;
  268. }
  269. frompcindex = (uint32_t*) &p->froms[((unsigned long) frompcindex)
  270. / (HASHFRACTION * sizeof(*p->froms))];
  271. toindex = *((unsigned short*) frompcindex); /* get froms[] value */
  272. if (toindex == 0) {
  273. /*
  274. * first time traversing this arc
  275. */
  276. toindex = ++p->tos[0].link; /* the link of tos[0] points to the last used record in the array */
  277. if (toindex >= p->tolimit) { /* more tos[] entries than we can handle! */
  278. goto overflow;
  279. }
  280. *((unsigned short*) frompcindex) = (unsigned short) toindex; /* store new 'to' value into froms[] */
  281. top = &p->tos[toindex];
  282. top->selfpc = (size_t) selfpc;
  283. top->count = 1;
  284. top->link = 0;
  285. goto done;
  286. }
  287. top = &p->tos[toindex];
  288. if (top->selfpc == (size_t) selfpc) {
  289. /*
  290. * arc at front of chain; usual case.
  291. */
  292. top->count++;
  293. goto done;
  294. }
  295. /*
  296. * have to go looking down chain for it.
  297. * top points to what we are looking at,
  298. * prevtop points to previous top.
  299. * we know it is not at the head of the chain.
  300. */
  301. for (; /* goto done */;) {
  302. if (top->link == 0) {
  303. /*
  304. * top is end of the chain and none of the chain
  305. * had top->selfpc == selfpc.
  306. * so we allocate a new tostruct
  307. * and link it to the head of the chain.
  308. */
  309. toindex = ++p->tos[0].link;
  310. if (toindex >= p->tolimit) {
  311. goto overflow;
  312. }
  313. top = &p->tos[toindex];
  314. top->selfpc = (size_t) selfpc;
  315. top->count = 1;
  316. top->link = *((unsigned short*) frompcindex);
  317. *(unsigned short*) frompcindex = (unsigned short) toindex;
  318. goto done;
  319. }
  320. /*
  321. * otherwise, check the next arc on the chain.
  322. */
  323. prevtop = top;
  324. top = &p->tos[top->link];
  325. if (top->selfpc == (size_t) selfpc) {
  326. /*
  327. * there it is.
  328. * increment its count
  329. * move it to the head of the chain.
  330. */
  331. top->count++;
  332. toindex = prevtop->link;
  333. prevtop->link = top->link;
  334. top->link = *((unsigned short*) frompcindex);
  335. *((unsigned short*) frompcindex) = (unsigned short) toindex;
  336. goto done;
  337. }
  338. }
  339. done: p->state--;
  340. /* and fall through */
  341. out: return; /* normal return restores saved registers */
  342. overflow: p->state++; /* halt further profiling */
  343. #define TOLIMIT "mcount: tos overflow\n"
  344. write(STDOUT_FILENO, TOLIMIT, sizeof(TOLIMIT));
  345. goto out;
  346. }
  347. #define NUM_OCTETS_PER_LINE 20
  348. #define FLUSH_OUTPUT() fflush(stdout)
  349. static void hexdumpbuf(char *buf, unsigned long sz)
  350. {
  351. unsigned long rem, cur = 0, i = 0;
  352. FLUSH_OUTPUT();
  353. while (cur < sz) {
  354. rem = ((sz - cur) < NUM_OCTETS_PER_LINE) ? (sz - cur) : NUM_OCTETS_PER_LINE;
  355. for (i = 0; i < rem; i++) {
  356. printf("%02x", buf[cur + i]);
  357. }
  358. printf("\n");
  359. FLUSH_OUTPUT();
  360. cur += rem;
  361. }
  362. }
  363. long gprof_collect(unsigned long interface)
  364. {
  365. static const char gmon_out[] = "gmon.out";
  366. int fd = -1;
  367. int hz;
  368. int fromindex;
  369. int endfrom;
  370. size_t frompc;
  371. int toindex;
  372. struct rawarc rawarc;
  373. struct gmonparam *p = GMONPARAM;
  374. struct gmonhdr gmonhdr, *hdr;
  375. const char *proffile;
  376. char *bufptr;
  377. #ifdef DEBUG
  378. int log, len;
  379. char dbuf[200];
  380. #endif
  381. if (p->state == GMON_PROF_ERROR) {
  382. ERR("_mcleanup: tos overflow\n");
  383. return -1;
  384. }
  385. hz = PROF_HZ;
  386. moncontrol(0); /* stop */
  387. if (interface == 0) {
  388. gprof_data.size = 0;
  389. if (gprof_data.buf == NULL) {
  390. gprof_data.buf = malloc(sizeof(gmonhdr) + p->kcountsize + p->tolimit * sizeof(struct rawarc));
  391. if (gprof_data.buf == NULL) {
  392. ERR("gprof_collect: unable to malloc enough memory to store gprof data\n");
  393. return -1;
  394. }
  395. }
  396. bufptr = gprof_data.buf;
  397. } else if (interface == 1) {
  398. proffile = gmon_out;
  399. fd = open(proffile, O_CREAT | O_TRUNC | O_WRONLY, 0666);
  400. if (fd < 0) {
  401. printf("Unable to open %s\n", proffile);
  402. return -1;
  403. }
  404. } else {
  405. printf("\nDump profiling data start\n");
  406. }
  407. #ifdef DEBUG
  408. len = sprintf(dbuf, "[mcleanup1] kcount 0x%x ssiz %d\n",
  409. p->kcount, p->kcountsize);
  410. write(STDOUT_FILENO, dbuf, len);
  411. #endif
  412. hdr = (struct gmonhdr*) &gmonhdr;
  413. hdr->lpc = p->lowpc;
  414. hdr->hpc = p->highpc;
  415. hdr->ncnt = p->kcountsize + sizeof(gmonhdr);
  416. hdr->version = GMONVERSION;
  417. hdr->profrate = hz;
  418. if (interface == 0) {
  419. memcpy(bufptr, (void*) hdr, sizeof *hdr);
  420. bufptr += sizeof *hdr;
  421. memcpy(bufptr, (void*) p->kcount, p->kcountsize);
  422. bufptr += p->kcountsize;
  423. } else if (interface == 1) {
  424. write(fd, (const char*) hdr, sizeof *hdr);
  425. write(fd, (const char*) p->kcount, p->kcountsize);
  426. } else {
  427. hexdumpbuf((char*) hdr, sizeof *hdr);
  428. hexdumpbuf((char *)p->kcount, (unsigned long)p->kcountsize);
  429. }
  430. #ifdef DEBUG
  431. len = sprintf(dbuf, "[mcleanup1] lowpc 0x%lx highpc 0x%lx ncnt %d\n",
  432. hdr->lpc, hdr->hpc, hdr->ncnt);
  433. write(STDOUT_FILENO, dbuf, len);
  434. #endif
  435. endfrom = p->fromssize / sizeof(*p->froms);
  436. for (fromindex = 0; fromindex < endfrom; fromindex++) {
  437. if (p->froms[fromindex] == 0) {
  438. continue;
  439. }
  440. frompc = p->lowpc;
  441. frompc += fromindex * HASHFRACTION * sizeof(*p->froms);
  442. for (toindex = p->froms[fromindex]; toindex != 0; toindex =
  443. p->tos[toindex].link) {
  444. #ifdef DEBUG
  445. len = sprintf(dbuf,
  446. "[mcleanup2] frompc 0x%x selfpc 0x%x count %d\n" ,
  447. frompc, p->tos[toindex].selfpc,
  448. p->tos[toindex].count);
  449. write(STDOUT_FILENO, dbuf, len);
  450. #endif
  451. rawarc.raw_frompc = frompc;
  452. rawarc.raw_selfpc = p->tos[toindex].selfpc;
  453. rawarc.raw_count = p->tos[toindex].count;
  454. if (interface == 0) {
  455. memcpy(bufptr, (void*) &rawarc, sizeof rawarc);
  456. bufptr += sizeof rawarc;
  457. } else if (interface == 1) {
  458. write(fd, (const char*)&rawarc, sizeof rawarc);
  459. } else {
  460. hexdumpbuf((char *)(&rawarc), (unsigned long)(sizeof rawarc));
  461. }
  462. }
  463. }
  464. if (interface == 0) {
  465. gprof_data.size = bufptr - gprof_data.buf;
  466. printf("Collected gprof data @0x%lx, size %lu bytes\n", gprof_data.buf, gprof_data.size);
  467. } else if (interface == 1) {
  468. close(fd);
  469. printf("Write %s done!\n", gmon_out);
  470. } else {
  471. printf("\nCREATE: %s\n", gmon_out);
  472. printf("\nDump profiling data finished\n");
  473. }
  474. return 0;
  475. }
  476. /* sample the current program counter */
  477. void gprof_sample(unsigned long pc)
  478. {
  479. size_t idx;
  480. struct gmonparam *p = GMONPARAM;
  481. if (p->state == GMON_PROF_ON) {
  482. if (pc >= p->lowpc && pc < p->highpc) {
  483. idx = PROFIDX(pc, p->lowpc, p->scale);
  484. p->kcount[idx]++;
  485. }
  486. }
  487. }