]>
jfr.im git - irc/quakenet/snircd.git/blob - tools/ringlog.c
2 ** Copyright (C) 2002 by Kevin L. Mitchell <klmitch@mit.edu>
4 ** This program is free software; you can redistribute it and/or modify
5 ** it under the terms of the GNU General Public License as published by
6 ** the Free Software Foundation; either version 2 of the License, or
7 ** (at your option) any later version.
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.
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, MA 02111-1307 USA
18 ** @(#)$Id: ringlog.c,v 1.4 2004/07/01 12:38:28 entrope Exp $
21 * This file contains two separate pieces, along with some common
22 * gunk. If RINGLOG_INSTRUMENT is defined, the two special functions
23 * __cyg_profile_func_enter() and __cyg_profile_func_exit() are
24 * defined; otherwise a main function is defined. The common gunk is
25 * init_log(), which opens and, if necessary, initializes a special
26 * binary log file that is treated as a ring buffer (to prevent the
27 * file from growing unboundedly).
29 * The object produced when compiled with RINGLOG_INSTRUMENT is
30 * designed to work with the special gcc option
31 * -finstrument-functions; this option causes the
32 * __cyg_profile_func_*() functions mentioned above to be called when
33 * a function is entered or exited. (Of course, ringlog.o should
34 * *not* be compiled with this option.) These functions will in turn
35 * call store_entry(), which will call init_log() as needed to open
36 * the log file, ensure that a start record is output, and then will
37 * store records for the function calls. The log file used is
38 * "call.ringlog" in the directory from which the program was
41 * When RINGLOG_INSTRUMENT is *not* defined while building, a main
42 * function is defined, and the result is an executable for
43 * interpretation of a ringlog. Usage is very simple: All arguments
44 * not beginning with '-' are treated as files to open, and all
45 * arguments beginning with '-' are treated as a specification for the
46 * number of entries new files should be created with. If this
47 * specification is 0 (which it is by default), files will not be
48 * created if they do not already exist.
50 * For every filename argument, at least one line will be printed
51 * out. If the file is not empty, the entries in the file will be
52 * printed out, one to a line. Each entry is numbered with a logical
53 * number. The entry numbers are followed by a two word description
54 * of the entry type ("Log start," "Function entry," "Function exit,"
55 * and "Invalid entry"), followed by a colon (":"), followed by the
56 * word "addr" and the address of the function, followed by the word
57 * "call" and the address from which the function was called. The
58 * ringlog program is not able to convert these addresses to symbols
59 * or file and line numbers--that can be done with a program like
60 * addr2line (part of the binutils package). The output has been
61 * carefully contrived to be parsable by a script.
63 * The file format is documented below. Note that data is stored in
67 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
68 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
70 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
71 * | First entry | Last entry |
72 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
77 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
81 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
82 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
84 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
85 * | Function address |
88 * | Function address |
89 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
90 * | Calling location |
93 * | Calling location |
94 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
96 * Some systems may have pointers larger than 32 bits, which is why these
97 * fields are allowed to be variable width.
105 #include <sys/mman.h>
106 #include <sys/stat.h>
107 #include <sys/types.h>
112 * 0 belong 0x52e45fd4 RingLog call trace file, big endian
113 * >4 beshort x (First entry %u,
114 * >>6 beshort x last entry %u)
115 * 0 lelong 0x52e45fd4 RingLog call trace file, little endian
116 * >4 leshort x (First entry %u,
117 * >>6 leshort x last entry %u)
119 #define RINGLOG_MAGIC 0x52e45fd4 /* verify file format */
121 #define RINGLOG_INIT 0x00000000 /* mark when a session was initiated */
122 #define RINGLOG_ENTER 0x01010101 /* record function entry */
123 #define RINGLOG_EXIT 0x02020202 /* record function exit */
125 #define RINGLOG_FNAME "call.ringlog" /* default file name */
126 #define RINGLOG_ILEN 2000 /* default number of entries */
128 /* length of the header and of individual entries */
129 #define HEAD_LEN (sizeof(u_int32_t) + 2 * sizeof(u_int16_t))
130 #define ENTRY_LEN (sizeof(u_int32_t) + 2 * sizeof(void *))
132 /* return an lvalue to the specified type stored at the specified location */
133 #define rl_ref(log, loc, type) (*((type *)((log) + (loc))))
135 /* access particular header fields */
136 #define rl_magic(log) rl_ref((log), 0, u_int32_t)
137 #define rl_first(log) rl_ref((log), 4, u_int16_t)
138 #define rl_last(log) rl_ref((log), 6, u_int16_t)
140 /* translate physical entry number to a file location */
141 #define rl_addr(loc) ((loc) * ENTRY_LEN + HEAD_LEN)
143 /* extract the type, function, and call fields of an entry */
144 #define rl_type(log, loc) rl_ref((log), rl_addr(loc), u_int32_t)
145 #define rl_func(log, loc) rl_ref((log), rl_addr(loc) + sizeof(u_int32_t), \
147 #define rl_call(log, loc) rl_ref((log), rl_addr(loc) + sizeof(u_int32_t) + \
148 sizeof(void *), void *)
150 static char *log
= 0; /* the log has to be global data */
151 static size_t log_size
= 0; /* remember the size of the log */
152 static int log_length
= 0; /* remember how many entries it'll hold */
154 /* Open and initialize the log file */
156 init_log(char *fname
, size_t init_len
)
159 int fd
, err
= 0, size
= -1;
163 if ((fd
= open(fname
, O_RDWR
| (init_len
> 0 ? O_CREAT
: 0),
164 S_IRUSR
| S_IWUSR
)) < 0)
165 return errno
; /* return error */
167 if (fstat(fd
, &buf
)) { /* get size */
168 err
= errno
; /* save errno... */
169 close(fd
); /* close file descriptor */
170 return err
; /* return error */
173 if (buf
.st_size
<= 8) /* too small */
174 size
= HEAD_LEN
+ ENTRY_LEN
* init_len
;
175 else if ((buf
.st_size
- 8) % ENTRY_LEN
) /* not a multiple of entry length */
176 size
= ((buf
.st_size
- 8) / ENTRY_LEN
+ 1) * ENTRY_LEN
+ 8; /* round up */
178 if (size
>= 0) { /* need to set the size */
179 if (lseek(fd
, size
- 1, SEEK_SET
) < 0) { /* seek to the end of our file */
180 err
= errno
; /* save errno... */
181 close(fd
); /* close file descriptor */
182 return err
; /* return error */
185 if (write(fd
, &c
, 1) < 0) { /* write a zero to set the new size */
186 err
= errno
; /* save errno... */
187 close(fd
); /* close file descriptor */
188 return err
; /* return error */
191 log_size
= size
; /* record log size */
193 log_size
= buf
.st_size
; /* record log size */
195 /* map the file to memory */
196 if ((log
= (char *)mmap(0, log_size
, PROT_READ
| PROT_WRITE
,
197 MAP_SHARED
, fd
, 0)) == MAP_FAILED
)
198 err
= errno
; /* save errno... */
200 close(fd
); /* don't need the file descriptor anymore */
202 if (err
) /* an error occurred while mapping the file; return it */
205 log_length
= (log_size
- HEAD_LEN
) / ENTRY_LEN
; /* store number of entries */
207 if (rl_magic(log
) == 0) { /* initialize if necessary */
208 rl_magic(log
) = RINGLOG_MAGIC
;
213 if (rl_magic(log
) != RINGLOG_MAGIC
) { /* verify file format */
214 munmap(log
, log_size
); /* unmap file */
215 return -1; /* -1 indicates file format error */
218 return 0; /* return success */
221 #ifdef RINGLOG_INSTRUMENT
223 /* store an entry in the log file */
225 store_entry(u_int32_t type
, void *this_fn
, void *call_site
)
227 if (!log
) { /* open the log file if necessary; die if unable */
228 assert(init_log(RINGLOG_FNAME
, RINGLOG_ILEN
) == 0);
229 store_entry(RINGLOG_INIT
, 0, 0); /* mark start of logging */
232 if (++(rl_last(log
)) >= log_length
) /* select next entry to fill */
233 rl_last(log
) = 0; /* wrap if needed */
235 if (rl_first(log
) == rl_last(log
)) { /* advance start pointer if collision */
236 if (++(rl_first(log
)) >= log_length
) /* wrap if necessary */
238 } else if (rl_first(log
) == (u_int16_t
)-1) /* no entries yet; enter one */
241 rl_type(log
, rl_last(log
)) = type
; /* record the entry */
242 rl_func(log
, rl_last(log
)) = this_fn
;
243 rl_call(log
, rl_last(log
)) = call_site
;
246 /* called upon function entry */
248 __cyg_profile_func_enter(void *this_fn
, void *call_site
)
250 store_entry(RINGLOG_ENTER
, this_fn
, call_site
);
253 /* called upon function exit */
255 __cyg_profile_func_exit(void *this_fn
, void *call_site
)
257 store_entry(RINGLOG_EXIT
, this_fn
, call_site
);
260 #else /* !defined(RINGLOG_INSTRUMENT) */
262 /* converts a type to a printable string */
264 get_type(u_int32_t type
)
268 return " Logging start";
271 return "Function entry";
274 return " Function exit";
278 return " Invalid entry";
281 /* Print out entries from a starting point to an end point */
283 extract(int *count
, u_int16_t start
, u_int16_t end
)
285 for (; start
<= end
; start
++)
286 printf("% 4d %s: addr %p call %p\n", (*count
)++,
287 get_type(rl_type(log
, start
)), rl_func(log
, start
),
288 rl_call(log
, start
));
292 main(int argc
, char **argv
)
295 int i
, err
, size
= 0;
297 while ((arg
= *++argv
)) {
298 if (arg
[0] == '-') { /* -<number> turns into log file size */
299 size
= atoi(arg
+ 1);
303 log
= 0; /* initialize our data */
307 switch ((err
= init_log(arg
, size
))) { /* initialize the log */
308 case -1: /* file is in an invalid format */
309 printf("File %s not a valid ringlog file\n", arg
);
313 case 0: /* file has opened and is ready to be read */
316 default: /* some error occurred */
317 printf("Error %d opening file %s: %s\n", err
, arg
, strerror(err
));
322 if (rl_first(log
) == (u_int16_t
)-1) /* it's an empty file */
323 printf("File %s is empty\n", arg
);
324 else { /* print out file contents */
325 printf("File %s contents:\n", arg
);
327 i
= 0; /* initialize counter */
328 if (rl_last(log
) <= rl_first(log
)) { /* print out log file */
329 extract(&i
, rl_first(log
), log_length
- 1); /* end of buffer... */
330 extract(&i
, 0, rl_last(log
)); /* then beginning of buffer */
332 extract(&i
, rl_first(log
), rl_last(log
));
335 munmap(log
, log_size
); /* unmap the file */
341 #endif /* !RINGLOG_INSTRUMENT */