Name: Event Logging Macros II Author: Rusty Russell and Werner "Macromancer" Almesberger Status: Experimental Depends: D: This is an example implementation of event logging macros. The D: idea is that drivers can register and report problems via an D: interface which provides both nicely-formatted printk() messages, D: and structured message numbers and data logging for those who like D: that kind of stuff. D: D: This is a much nicer version than previously. Thanks David! D: Evil printk version thanks to Werner Almesberger. diff -urpN --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal .2579-linux-2.5.31/Makefile .2579-linux-2.5.31.updated/Makefile --- .2579-linux-2.5.31/Makefile 2002-08-11 15:31:29.000000000 +1000 +++ .2579-linux-2.5.31.updated/Makefile 2002-08-26 18:56:52.000000000 +1000 @@ -207,6 +207,10 @@ ifeq ($(filter $(noconfig_targets),$(MAK @echo '***' @exit 1 +ifdef CONFIG_EVENTLOGGING +LOG_SYMBOLS := kernel/generated-log_symbols.o +endif + # # INSTALL_PATH specifies where to place the updated kernel and system map # images. Uncomment if you want to place them anywhere other than root. @@ -276,6 +280,7 @@ cmd_link_vmlinux = $(LD) $(LDFLAGS) $(LD $(DRIVERS) \ $(NETWORKS) \ --end-group \ + $(LOG_SYMBOLS) \ -o vmlinux # set -e makes the rule exit immediately on error @@ -291,7 +296,7 @@ define rule_link_vmlinux $(NM) vmlinux | grep -v '\(compiled\)\|\(\.o$$\)\|\( [aUw] \)\|\(\.\.ng$$\)\|\(LASH[RL]DI\)' | sort > System.map endef -vmlinux: $(vmlinux-objs) FORCE +vmlinux: $(vmlinux-objs) $(LOG_SYMBOLS) FORCE $(call if_changed_rule,link_vmlinux) # The actual objects are generated when descending, @@ -498,6 +503,16 @@ define generate-asm-offsets.h echo "#endif" ) endef +# Event logging +# --------------------------------------------------------------------------- +kernel/generated-log_symbols.c: scripts/generate_logsymbols FORCE + scripts/generate_logsymbols generated-log.map \ + init/main.o init/version.o \ + $(CORE_FILES) $(DRIVERS) $(NETWORKS) $(LIBS) > $@ + +scripts/generate_logsymbols: scripts/generate_logsymbols.c + $(HOSTCC) $(HOSTCFLAGS) -o $@ $< -lbfd + # RPM target # --------------------------------------------------------------------------- diff -urpN --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal .2579-linux-2.5.31/drivers/char/Makefile .2579-linux-2.5.31.updated/drivers/char/Makefile --- .2579-linux-2.5.31/drivers/char/Makefile 2002-08-11 15:31:34.000000000 +1000 +++ .2579-linux-2.5.31.updated/drivers/char/Makefile 2002-08-26 18:56:52.000000000 +1000 @@ -7,7 +7,7 @@ # FONTMAPFILE = cp437.uni -obj-y += mem.o tty_io.o n_tty.o tty_ioctl.o pty.o misc.o random.o +obj-y += mem.o tty_io.o n_tty.o tty_ioctl.o pty.o misc.o random.o logtest.o # All of the (potential) objects that export symbols. # This list comes from 'grep -l EXPORT_SYMBOL *.[hc]'. diff -urpN --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal .2579-linux-2.5.31/drivers/char/logtest.c .2579-linux-2.5.31.updated/drivers/char/logtest.c --- .2579-linux-2.5.31/drivers/char/logtest.c 1970-01-01 10:00:00.000000000 +1000 +++ .2579-linux-2.5.31.updated/drivers/char/logtest.c 2002-08-26 18:56:52.000000000 +1000 @@ -0,0 +1,98 @@ +/* Template driver showing how to use new logging interface. + + Copyright (C) 2002 Rusty Russell IBM + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ +#include + +#include +#include +#include +#include + +/* This is kept in the private pointer */ +struct private_stuff +{ + int num1; + unsigned int num2; +}; + +static struct private_stuff *template_priv(struct net_device *dev) +{ + return dev->priv; +} + +static int template_xmit(struct sk_buff *skb, struct net_device *dev) +{ + if (skb == NULL) + problem(dev, "NULL xmit problem!", + detail(dev, "%p", dev), detail(skb, "%p", skb)); + problem(dev, "No problem"); + return 0; +} + +static int __init template_init(struct net_device *dev) +{ + /* Initialize the device structure. */ + dev->priv = kmalloc(sizeof(struct private_stuff), GFP_KERNEL); + if (dev->priv == NULL) + return -ENOMEM; + memset(dev->priv, 0, sizeof(struct private_stuff)); + + /* Fill in device structure with ethernet-generic values. */ + ether_setup(dev); + dev->tx_queue_len = 0; + dev->flags |= IFF_NOARP; + dev->flags &= ~IFF_MULTICAST; + + introduce(dev, "template driver", + detail(queue_len, "%lu", dev->tx_queue_len)); + template_priv(dev)->num1 = -7; + template_priv(dev)->num2 = 2; + + template_xmit(NULL, dev); + return 0; +} + +static struct net_device dev_template; + +static int __init template_init_module(void) +{ + int err; + + dev_template.init = template_init; + SET_MODULE_OWNER(&dev_template); + + /* Find a name for this unit */ + err=dev_alloc_name(&dev_template,"template%d"); + if(err<0) + return err; + err = register_netdev(&dev_template); + if (err<0) + return err; + return 0; +} + +static void __exit template_cleanup_module(void) +{ + unregister_netdev(&dev_template); + unintroduce(&dev_template); + kfree(dev_template.priv); +} + +module_init(template_init_module); +module_exit(template_cleanup_module); +MODULE_LICENSE("GPL"); diff -urpN --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal .2579-linux-2.5.31/include/linux/log.h .2579-linux-2.5.31.updated/include/linux/log.h --- .2579-linux-2.5.31/include/linux/log.h 1970-01-01 10:00:00.000000000 +1000 +++ .2579-linux-2.5.31.updated/include/linux/log.h 2002-08-26 19:08:57.000000000 +1000 @@ -0,0 +1,140 @@ +#ifndef _LINUX_LOG_H +#define _LINUX_LOG_H +#include +#include + +/* Simple interface for more structured reporting: + introduce(uniqueptr, "name", + detail(name, "format", expr), ...); + + For errors which did not result in request failure or upper levels + being notified, use: + recovered(uniqueptr, "name", detail(name, "format", expr), ...); + + Otherwise (or if you don't know), use: + problem(uniqueptr, "string", detail(name, "format", expr), ...); + + To remove a reporting object: + unintroduce(uniqueptr); +*/ + +#ifndef CONFIG_EVENTLOGGING + +/* Do not meddle in the affairs of cpp, for it is subtle, and quick to anger */ +#define __detS_ +#define __detV_ + +#define __detS_detail(label,format,value) " " #label "=" format +#define __detV_detail(label,format,value) value, + +#define __detV_3(detail) detail +#define __detV_2(detail,...) detail __detV_3( __detV_ ## __VA_ARGS__) +#define __detV_1(detail,...) detail __detV_2( __detV_ ## __VA_ARGS__) + +#define __detS_3(detail) detail +#define __detS_2(detail,...) detail __detS_3( __detS_ ## __VA_ARGS__) +#define __detS_1(detail,...) detail __detS_2( __detS_ ## __VA_ARGS__) + +#define recovered(id, msg, ...) \ + printk(KERN_WARNING "%p:%s:" __detS_1( __detS_ ## __VA_ARGS__) "%c", \ + id, msg, __detV_1( __detV_ ## __VA_ARGS__) '\n') + +#define problem(id, msg, ...) \ + printk(KERN_ERR "%p:%s:" __detS_1( __detS_ ## __VA_ARGS__) "%c", \ + id, msg, __detV_1( __detV_ ## __VA_ARGS__) '\n') + +#define introduce(id, name, ...) \ + printk(KERN_INFO "Introducing %p:%s:" \ + __detS_1( __detS_ ## __VA_ARGS__) "%c", \ + id, msg, __detV_1( __detV_ ## __VA_ARGS__) '\n') + +#define unintroduce(id) \ + printk(KERN_INFO "Unintroducing %p\n", id) + +#else +/* Paste three symbols together to form identifier. */ +#define __PASTE4_(w,x,y,z) w##_##x##_##y##_##z +#define __PASTE4(w,x,y,z) __PASTE4_(w,x,y,z) + +#define __log_id __PASTE4(__log_id,KBUILD_OBJECT,KBUILD_BASENAME,__LINE__) +#define __log_template __PASTE4(__log_template,KBUILD_OBJECT,KBUILD_BASENAME,__LINE__) + +struct log_templ +{ + const char *name; + const char *format; +}; + +void __introduce(void *id, const char *log_id, const struct log_templ *templ, + ...); +void __problem(void *id, const char *log_id, const struct log_templ *templ, + ...); + +/* This does the printf-style typechecking */ +static void __checkformat(const char *,...)__attribute__((format(printf,1,2))); +static inline void __checkformat(const char *fmt,...) { } + +/* Bloat doesn't matter: this doesn't end up in vmlinux */ +/* These three required to figure out which details() go with + which problem() */ +struct log_position +{ + int line; + char modname[128 - sizeof(int)]; + char file[128]; +}; + +#define _LOG_POS { __LINE__, __stringify(KBUILD_OBJECT), __FILE__ } + +struct log_info +{ + char name[128]; + char format[128]; /* NULL for problem() or introduce() entry */ + struct log_position pos; +}; + +/* Insert information in section as a sideeffect. Pass the name (to + locate the type) and the expression. Use typeof to avoid + evaluating expression twice. */ +#define detail(name, format, expr) \ +__stringify(name), \ +({ \ + typeof(expr) __expr; \ + static struct log_info __attribute__((section(".log"), unused)) __ \ + = { __stringify(name), format, _LOG_POS }; \ + (void *)&__expr; /* Avoid uninitialized warning */ \ + __checkformat(format, __expr); \ + expr; \ +}) + +#define recovered(id, string, ...) \ +do { \ + extern const char *__log_id; \ + extern struct log_templ __log_template[]; \ + static struct log_info __attribute__((section(".log"),unused)) ___ \ + = { string, "", _LOG_POS }; \ + __recovered(id, __log_id, __log_template , ## __VA_ARGS__); \ +} while(0) + +#define problem(id, string, ...) \ +do { \ + extern const char *__log_id; \ + extern struct log_templ __log_template[]; \ + static struct log_info __attribute__((section(".log"),unused)) ___ \ + = { string, "", _LOG_POS }; \ + __problem(id, __log_id, __log_template , ## __VA_ARGS__); \ +} while(0) + +#define introduce(id, string, ...) \ +do { \ + extern const char *__log_id; \ + extern struct log_templ __log_template[]; \ + static struct log_info __attribute__((section(".log"),unused)) ___ \ + = { string, "", _LOG_POS }; \ + __introduce(id, __log_id, __log_template , ## __VA_ARGS__); \ +} while(0) + +extern void unintroduce(void *id); +#endif /* CONFIG_EVENTLOGGING */ + +#endif /*_LINUX_LOG_H*/ diff -urpN --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal .2579-linux-2.5.31/init/Config.in .2579-linux-2.5.31.updated/init/Config.in --- .2579-linux-2.5.31/init/Config.in 2002-02-20 17:56:42.000000000 +1100 +++ .2579-linux-2.5.31.updated/init/Config.in 2002-08-26 18:56:52.000000000 +1000 @@ -9,6 +9,7 @@ bool 'Networking support' CONFIG_NET bool 'System V IPC' CONFIG_SYSVIPC bool 'BSD Process Accounting' CONFIG_BSD_PROCESS_ACCT bool 'Sysctl support' CONFIG_SYSCTL +dep_bool 'Eventlogging (EXPERIMENTAL)' CONFIG_EVENTLOGGING $CONFIG_EXPERIMENTAL endmenu mainmenu_option next_comment diff -urpN --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal .2579-linux-2.5.31/kernel/Makefile .2579-linux-2.5.31.updated/kernel/Makefile --- .2579-linux-2.5.31/kernel/Makefile 2002-08-11 15:31:43.000000000 +1000 +++ .2579-linux-2.5.31.updated/kernel/Makefile 2002-08-26 18:57:51.000000000 +1000 @@ -10,13 +10,14 @@ O_TARGET := kernel.o export-objs = signal.o sys.o kmod.o context.o ksyms.o pm.o exec_domain.o \ - printk.o platform.o suspend.o dma.o + printk.o platform.o suspend.o dma.o log.o obj-y = sched.o fork.o exec_domain.o panic.o printk.o \ module.o exit.o itimer.o time.o softirq.o resource.o \ sysctl.o capability.o ptrace.o timer.o user.o \ signal.o sys.o kmod.o context.o futex.o platform.o +obj-$(CONFIG_EVENTLOGGING) += log.o obj-$(CONFIG_GENERIC_ISA_DMA) += dma.o obj-$(CONFIG_SMP) += cpu.o obj-$(CONFIG_UID16) += uid16.o diff -urpN --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal .2579-linux-2.5.31/kernel/log.c .2579-linux-2.5.31.updated/kernel/log.c --- .2579-linux-2.5.31/kernel/log.c 1970-01-01 10:00:00.000000000 +1000 +++ .2579-linux-2.5.31.updated/kernel/log.c 2002-08-26 19:09:24.000000000 +1000 @@ -0,0 +1,88 @@ +#include +#include +#include +#include + +static const char *get_format(const struct log_templ *templ, + const char *name) +{ + unsigned int i; + + /* Find matching name. */ + for (i = 0; strcmp(name, templ[i].name) != 0; i++) { + if (!templ[i+1].name) { + printk("Couldn't find name `%s'\n", name); + BUG(); + } + } + return templ[i].format; +} + +static void test_dump_args(const struct log_templ *templ, va_list args) +{ + unsigned int i; + char buffer[256] = ""; + + /* One arg for each one in format: don't know order. */ + for (i = 0; templ[i].format; i++) { + const char *name = (char *)va_arg(args, char *); + const char *format; + + format = get_format(templ, name); + if (strcmp(format, "%lu") == 0) { + unsigned long i + = (unsigned long)va_arg(args, unsigned long); + sprintf(buffer + strlen(buffer), "%lu:", i); + } else if (strcmp(format, "%p") == 0) { + void *p = (void *)va_arg(args, void *); + sprintf(buffer + strlen(buffer), "%p:", p); + } else { + sprintf(buffer + strlen(buffer), "*UNKNOWN*"); + break; + } + } + printk("%s\n", buffer); +} + +void __introduce(void *id, const char *log_id, + const struct log_templ *templ, + ...) +{ + va_list args; + + printk("Introducing: %p %s ", id, log_id); + va_start(args, templ); + test_dump_args(templ, args); + va_end(args); +} + +void __recovered(void *id, const char *log_id, const struct log_templ *templ, + ...) +{ + va_list args; + + printk("Recovered: %p %s ", id, log_id); + va_start(args, templ); + test_dump_args(templ, args); + va_end(args); +} + +void __problem(void *id, const char *log_id, const struct log_templ *templ, + ...) +{ + va_list args; + + printk("Problem: %p %s ", id, log_id); + va_start(args, templ); + test_dump_args(templ, args); + va_end(args); +} + +void unintroduce(void *id) +{ + printk("Unintroducing: %p\n", id); +} + +EXPORT_SYMBOL(__introduce); +EXPORT_SYMBOL(__problem); +EXPORT_SYMBOL(unintroduce); diff -urpN --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal .2579-linux-2.5.31/scripts/generate_logsymbols.c .2579-linux-2.5.31.updated/scripts/generate_logsymbols.c --- .2579-linux-2.5.31/scripts/generate_logsymbols.c 1970-01-01 10:00:00.000000000 +1000 +++ .2579-linux-2.5.31.updated/scripts/generate_logsymbols.c 2002-08-26 18:56:52.000000000 +1000 @@ -0,0 +1,334 @@ +/* This is a quick example program which extracts log data from object + files and produces the symbols which are needed to link the + kernel. */ +#include +#include +#include +#include +#include + +/* MD4 code stolen from Andrew Tridgell's rsync (GPL). */ +struct mdfour { + uint32_t A, B, C, D; + uint32_t totalN; +}; + +static struct mdfour *m; + +#define MASK32 (0xffffffff) + +#define F(X,Y,Z) ((((X)&(Y)) | ((~(X))&(Z)))) +#define G(X,Y,Z) ((((X)&(Y)) | ((X)&(Z)) | ((Y)&(Z)))) +#define H(X,Y,Z) (((X)^(Y)^(Z))) +#define lshift(x,s) (((((x)<<(s))&MASK32) | (((x)>>(32-(s)))&MASK32))) + +#define ROUND1(a,b,c,d,k,s) a = lshift((a + F(b,c,d) + M[k])&MASK32, s) +#define ROUND2(a,b,c,d,k,s) a = lshift((a + G(b,c,d) + M[k] + 0x5A827999)&MASK32,s) +#define ROUND3(a,b,c,d,k,s) a = lshift((a + H(b,c,d) + M[k] + 0x6ED9EBA1)&MASK32,s) + +/* this applies md4 to 64 byte chunks */ +static void mdfour64(uint32_t *M) +{ + uint32_t AA, BB, CC, DD; + uint32_t A,B,C,D; + + A = m->A; B = m->B; C = m->C; D = m->D; + AA = A; BB = B; CC = C; DD = D; + + ROUND1(A,B,C,D, 0, 3); ROUND1(D,A,B,C, 1, 7); + ROUND1(C,D,A,B, 2, 11); ROUND1(B,C,D,A, 3, 19); + ROUND1(A,B,C,D, 4, 3); ROUND1(D,A,B,C, 5, 7); + ROUND1(C,D,A,B, 6, 11); ROUND1(B,C,D,A, 7, 19); + ROUND1(A,B,C,D, 8, 3); ROUND1(D,A,B,C, 9, 7); + ROUND1(C,D,A,B, 10, 11); ROUND1(B,C,D,A, 11, 19); + ROUND1(A,B,C,D, 12, 3); ROUND1(D,A,B,C, 13, 7); + ROUND1(C,D,A,B, 14, 11); ROUND1(B,C,D,A, 15, 19); + + + ROUND2(A,B,C,D, 0, 3); ROUND2(D,A,B,C, 4, 5); + ROUND2(C,D,A,B, 8, 9); ROUND2(B,C,D,A, 12, 13); + ROUND2(A,B,C,D, 1, 3); ROUND2(D,A,B,C, 5, 5); + ROUND2(C,D,A,B, 9, 9); ROUND2(B,C,D,A, 13, 13); + ROUND2(A,B,C,D, 2, 3); ROUND2(D,A,B,C, 6, 5); + ROUND2(C,D,A,B, 10, 9); ROUND2(B,C,D,A, 14, 13); + ROUND2(A,B,C,D, 3, 3); ROUND2(D,A,B,C, 7, 5); + ROUND2(C,D,A,B, 11, 9); ROUND2(B,C,D,A, 15, 13); + + ROUND3(A,B,C,D, 0, 3); ROUND3(D,A,B,C, 8, 9); + ROUND3(C,D,A,B, 4, 11); ROUND3(B,C,D,A, 12, 15); + ROUND3(A,B,C,D, 2, 3); ROUND3(D,A,B,C, 10, 9); + ROUND3(C,D,A,B, 6, 11); ROUND3(B,C,D,A, 14, 15); + ROUND3(A,B,C,D, 1, 3); ROUND3(D,A,B,C, 9, 9); + ROUND3(C,D,A,B, 5, 11); ROUND3(B,C,D,A, 13, 15); + ROUND3(A,B,C,D, 3, 3); ROUND3(D,A,B,C, 11, 9); + ROUND3(C,D,A,B, 7, 11); ROUND3(B,C,D,A, 15, 15); + + A += AA; B += BB; + C += CC; D += DD; + + A &= MASK32; B &= MASK32; + C &= MASK32; D &= MASK32; + + m->A = A; m->B = B; m->C = C; m->D = D; +} + +static void copy64(uint32_t *M, const unsigned char *in) +{ + int i; + + for (i=0;i<16;i++) + M[i] = (in[i*4+3]<<24) | (in[i*4+2]<<16) | + (in[i*4+1]<<8) | (in[i*4+0]<<0); +} + +static void copy4(unsigned char *out,uint32_t x) +{ + out[0] = x&0xFF; + out[1] = (x>>8)&0xFF; + out[2] = (x>>16)&0xFF; + out[3] = (x>>24)&0xFF; +} + +static void mdfour_begin(struct mdfour *md) +{ + md->A = 0x67452301; + md->B = 0xefcdab89; + md->C = 0x98badcfe; + md->D = 0x10325476; + md->totalN = 0; +} + +static void mdfour_tail(const unsigned char *in, int n) +{ + unsigned char buf[128]; + uint32_t M[16]; + uint32_t b; + + m->totalN += n; + + b = m->totalN * 8; + + memset(buf, 0, 128); + if (n) memcpy(buf, in, n); + buf[n] = 0x80; + + if (n <= 55) { + copy4(buf+56, b); + copy64(M, buf); + mdfour64(M); + } else { + copy4(buf+120, b); + copy64(M, buf); + mdfour64(M); + copy64(M, buf+64); + mdfour64(M); + } +} + +static void mdfour_update(struct mdfour *md, const unsigned char *in, int n) +{ + uint32_t M[16]; + + if (n == 0) mdfour_tail(in, n); + + m = md; + + while (n >= 64) { + copy64(M, in); + mdfour64(M); + in += 64; + n -= 64; + m->totalN += 64; + } + + if (n) mdfour_tail(in, n); +} + + +static void mdfour_result(struct mdfour *md, unsigned char *out) +{ + m = md; + + copy4(out, m->A); + copy4(out+4, m->B); + copy4(out+8, m->C); + copy4(out+12, m->D); +} + +/* From linux/log.h */ +struct log_position +{ + int line; + char modname[128 - sizeof(int)]; + char file[128]; +}; + +struct log_info +{ + char name[128]; + char format[128]; /* NULL for problem() or introduce() entry */ + struct log_position pos; +}; + +/* Return readable string representing hash of message */ +const char *hash_message(const char *modname, const char *message) +{ + static char map[64] = + "0123456789ABCDEFGHIJKLMNOPQRSTUV" + "WXYZabcdefghijklmnopqrstuvwxyz$@"; + struct mdfour md; + unsigned char rawout[16]; + static char text_out[17]; + unsigned int i; + + mdfour_begin(&md); + mdfour_update(&md, modname, strlen(modname)+1); + mdfour_update(&md, message, strlen(message)); + mdfour_result(&md, rawout); + + /* We actually drop 2 out of 8 bits */ + for (i = 0; i < 16; i++) + text_out[i] = map[rawout[i] % (sizeof(map)-1)]; + text_out[16] = '\0'; + return text_out; +} + +/* Create the C code for the log symbols on stdout. */ +static void create_log_entry(const struct log_info *info, + unsigned int num_details) +{ + unsigned int i; + + printf("/* %s: \"%s\" */\n", info->pos.file, info->name); + printf("const char *__log_id_%s_%u = \"%s\";\n", + info->pos.modname, info->pos.line, + hash_message(info->pos.modname, info->name)); + /* Encode the types being passed through ... in some form. + Simply use the printf-style for now */ + printf("const struct log_templ __log_template_%s_%u[] = {\n\t", + info->pos.modname, info->pos.line); + /* Second entry onwards contains details. */ + for (i = 0; i < num_details; i++) + printf("{ \"%s\", \"%s\" }, ", + info[i+1].name, info[i+1].format); + printf("{ 0, 0 } };\n"); +} + +/* Create the (external-to-kernel) mapping back to the messages and + names. */ +static void create_map(FILE *map, + const struct log_info *info, + unsigned int num_details) +{ + unsigned int i; + + fprintf(map, "%s:%s:%s:%u:", + hash_message(info->pos.modname, info->name), + info->pos.modname, info->pos.file, info->pos.line); + /* FIXME: escape any " in string. */ + fprintf(map, "\"%s\":", info->name); + for (i = 0; i < num_details; i++) + fprintf(map, "%s:", info[i+1].name); + fprintf(map, "\n"); +} + + +static int compare_loginfos(const void *va, const void *vb) +{ + const struct log_info *a = va, *b = vb; + int ret; + + ret = strcmp(a->pos.modname, b->pos.modname); + if (ret == 0) { + ret = strcmp(a->pos.file, b->pos.file); + if (ret == 0) + ret = a->pos.line - b->pos.line; + } + return ret; +} + +static unsigned int num_details(const struct log_info *info) +{ + unsigned int i; + + for (i = 1; info[i].format[0]; i++); + return i-1; +} + +static void do_section(bfd *bfd, asection *log, void *mapfile) +{ + bfd_size_type size; + size_t number; + struct log_info *contents, *i; + + /* Not log section? */ + if (strcmp(log->name, ".log") != 0) + return; + + size = bfd_section_size(bfd, log); + number = size / sizeof(struct log_info); + contents = malloc(size + sizeof(struct log_info)); + + if (!bfd_get_section_contents(bfd, log, contents, 0, size)) { + bfd_perror("Getting section contents"); + exit(1); + } + + /* Sort them into order, to ensure details for all problems adjacent */ + qsort(contents, number, sizeof(struct log_info), compare_loginfos); + + /* Terminator has format set to "" */ + strcpy(contents[number].name, ""); + + for (i = contents; i < contents + number; i += num_details(i) + 1) { + create_log_entry(i, num_details(i)); + create_map(mapfile, i, num_details(i)); + } + free(contents); +} + +int main(int argc, char *argv[]) +{ + unsigned int i; + FILE *map; + + bfd_init(); + map = fopen(argv[1], "w"); + fprintf(map, "# Auto-generated by generate_logsymbols\n"); + printf("/* Auto-generated by generate_logsymbols: don't edit.\n" + " * Size of info = 0x%02x.\n" + " */\n", sizeof(struct log_info)); + printf("#include \n\n"); + for (i = 2; i < argc; i++) { + bfd *obj; + + obj = bfd_openr(argv[i], NULL); + if (bfd_get_error()) { + bfd_perror("Opening file"); + exit(1); + } + /* You have to check format before using it. Crappy + interface. */ + if (bfd_check_format(obj, bfd_archive)) { + bfd *i, *nexti; + + for (i = bfd_openr_next_archived_file(obj, NULL); + i; + i = nexti) { + bfd_map_over_sections(i, do_section, map); + nexti = bfd_openr_next_archived_file(obj, i); + bfd_close(i); + bfd_set_error(bfd_error_no_error); + } + } else if (bfd_check_format(obj, bfd_object)) { + bfd_map_over_sections(obj, do_section, map); + } else { + bfd_perror("Identifying file"); + exit(1); + } + bfd_close(obj); + /* BFD's error caching is screwed. Grrr. */ + bfd_set_error(bfd_error_no_error); + } + return 0; +}