Hi,

After otto@ work on mallocdump using utrace(2), I started to look again at 
profiling (see moncontrol(2)).

The current implementation tries to write a gmon.out file at program exit(3) 
time, which isn't compatible with pledge(2) or unveil(2).

This diff changes the way the runtime profiling information is extracted by 
using utrace(2) (which is permitted while pledged).

The information is collected using ktrace(2), and the gmon.out file could be 
recreated from ktrace.out file post-execution (so without unveil(2) 
restriction).


One place where I needed to cheat a bit is at moncontrol(0) call in _mcleanup() 
function.

moncontrol(0) will stop profiling (change the state to GMON_PROF_OFF), and it 
is 
calling profil(2) to disable program counter profiling. _mcleanup() is the 
atexit(3) handler.

Instead of changing pledge(2) permission for profil(2) (which could go deep in 
the kernel to change the clockrate with setstatclockrate()), I just assumed 
that 
it isn't necessary to disable it here: we are in atexit(3), so the process is 
about to call _exit(2), and the kernel will stop the profiling itself if still 
running.


For post-execution gmon.out extraction, an helper might be needed: ktrace.out 
could contain multiple gmon.out files.

## compile and collect profil information (-tu option on ktrace is optional)
$ cc -static -pg test.c
$ ktrace -di -tu ./a.out

## get gmon.out file
$ kdump -u gmon.out | unvis > gmon.out

## get gmon.out.$name.$pid for multiple processes
##  - first get pid process-name
##  - extract each gmon.out for each pid and store in "gmon.out.$name.$pid" file
$ kdump -tu | sed -ne 's/^ \([0-9][0-9]*\) \([^ ]*\) .*/\1 \2/p' | sort -u \
        | while read pid name; do kdump -u gmon.out -p $pid | unvis > 
gmon.out.$name.$pid ; done


kdump diff from otto@ mallocdump is need for 'kdump -u label'.


Feedback would be appreciated.
-- 
Sebastien Marie


diff /home/semarie/repos/openbsd/src
commit - 7639070d00278d5f3d76cbc265d756c39619d7a8
path + /home/semarie/repos/openbsd/src
blob - f09ffd91837040d44fb17a9e8c0197c72ba9459a
file + lib/libc/gmon/gmon.c
--- lib/libc/gmon/gmon.c
+++ lib/libc/gmon/gmon.c
@@ -29,6 +29,7 @@
  */
 
 #include <sys/time.h>
+#include <sys/ktrace.h>
 #include <sys/gmon.h>
 #include <sys/mman.h>
 #include <sys/sysctl.h>
@@ -51,6 +52,32 @@ void
 PROTO_NORMAL(moncontrol);
 PROTO_DEPRECATED(monstartup);
 
+static void
+montrace(void *addr, size_t len)
+{
+       for (;;) {
+               if (len < KTR_USER_MAXLEN) {
+                       if (utrace("gmon.out", addr, len) == -1)
+                               ERR("error on utrace(), truncated gmon.out");
+                       return;
+               }
+               if (utrace("gmon.out", addr, KTR_USER_MAXLEN) == -1)
+                       ERR("error on utrace(), truncated gmon.out");
+               
+               len -= KTR_USER_MAXLEN;
+               addr += KTR_USER_MAXLEN;
+       }
+}
+
+#ifdef DEBUG
+static int
+monlog(char *msg)
+{
+       size_t len = strlen(msg);
+       return utrace("gmon.log", msg, len);
+}
+#endif
+
 void
 monstartup(u_long lowpc, u_long highpc)
 {
@@ -136,7 +163,6 @@ _mcleanup(void)
 void
 _mcleanup(void)
 {
-       int fd;
        int fromindex;
        int endfrom;
        u_long frompc;
@@ -147,11 +173,8 @@ _mcleanup(void)
        struct clockinfo clockinfo;
        const int mib[2] = { CTL_KERN, KERN_CLOCKRATE };
        size_t size;
-       char *profdir;
-       char *proffile;
-       char  buf[PATH_MAX];
 #ifdef DEBUG
-       int log, len;
+       int len;
        char dbuf[200];
 #endif
 
@@ -169,67 +192,16 @@ _mcleanup(void)
                clockinfo.profhz = clockinfo.hz;        /* best guess */
        }
 
-       moncontrol(0);
+       /*
+        * Do not call moncontrol(0) (neither profil(2)) as we might be pledged.
+        * We are in _mcleanup(), so the process is inside exit(3).
+        */
+       p->state = GMON_PROF_OFF;
 
-       if (issetugid() == 0 && (profdir = getenv("PROFDIR")) != NULL) {
-               char *s, *t, *limit;
-               pid_t pid;
-               long divisor;
-
-               /* If PROFDIR contains a null value, no profiling
-                  output is produced */
-               if (*profdir == '\0') {
-                       return;
-               }
-
-               limit = buf + sizeof buf - 1 - 10 - 1 -
-                   strlen(__progname) - 1;
-               t = buf;
-               s = profdir;
-               while((*t = *s) != '\0' && t < limit) {
-                       t++;
-                       s++;
-               }
-               *t++ = '/';
-
-               /*
-                * Copy and convert pid from a pid_t to a string.  For
-                * best performance, divisor should be initialized to
-                * the largest power of 10 less than PID_MAX.
-                */
-               pid = getpid();
-               divisor=10000;
-               while (divisor > pid) divisor /= 10;    /* skip leading zeros */
-               do {
-                       *t++ = (pid/divisor) + '0';
-                       pid %= divisor;
-               } while (divisor /= 10);
-               *t++ = '.';
-
-               s = __progname;
-               while ((*t++ = *s++) != '\0')
-                       ;
-
-               proffile = buf;
-       } else {
-               proffile = "gmon.out";
-       }
-
-       fd = open(proffile , O_CREAT|O_TRUNC|O_WRONLY, 0664);
-       if (fd == -1) {
-               perror( proffile );
-               return;
-       }
 #ifdef DEBUG
-       log = open("gmon.log", O_CREAT|O_TRUNC|O_WRONLY, 0664);
-       if (log == -1) {
-               perror("mcount: gmon.log");
-               close(fd);
-               return;
-       }
        snprintf(dbuf, sizeof dbuf, "[mcleanup1] kcount 0x%x ssiz %d\n",
            p->kcount, p->kcountsize);
-       write(log, dbuf, strlen(dbuf));
+       monlog(dbuf);
 #endif
        hdr = (struct gmonhdr *)&gmonhdr;
        bzero(hdr, sizeof(*hdr));
@@ -238,8 +210,8 @@ _mcleanup(void)
        hdr->ncnt = p->kcountsize + sizeof(gmonhdr);
        hdr->version = GMONVERSION;
        hdr->profrate = clockinfo.profhz;
-       write(fd, (char *)hdr, sizeof *hdr);
-       write(fd, p->kcount, p->kcountsize);
+       montrace(hdr, sizeof *hdr);
+       montrace(p->kcount, p->kcountsize);
        endfrom = p->fromssize / sizeof(*p->froms);
        for (fromindex = 0; fromindex < endfrom; fromindex++) {
                if (p->froms[fromindex] == 0)
@@ -254,15 +226,14 @@ _mcleanup(void)
                        "[mcleanup2] frompc 0x%x selfpc 0x%x count %d\n" ,
                                frompc, p->tos[toindex].selfpc,
                                p->tos[toindex].count);
-                       write(log, dbuf, strlen(dbuf));
+                       monlog(dbuf);
 #endif
                        rawarc.raw_frompc = frompc;
                        rawarc.raw_selfpc = p->tos[toindex].selfpc;
                        rawarc.raw_count = p->tos[toindex].count;
-                       write(fd, &rawarc, sizeof rawarc);
+                       montrace(&rawarc, sizeof rawarc);
                }
        }
-       close(fd);
 #ifdef notyet
        if (p->kcount != NULL) {
                munmap(p->kcount, p->kcountsize);

Reply via email to