gprof.c 20 KB

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