13ff40c12SJohn Marino /*
23ff40c12SJohn Marino * Backtrace debugging
33ff40c12SJohn Marino * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
43ff40c12SJohn Marino *
53ff40c12SJohn Marino * This software may be distributed under the terms of the BSD license.
63ff40c12SJohn Marino * See README for more details.
73ff40c12SJohn Marino */
83ff40c12SJohn Marino
9*a1157835SDaniel Fojt #ifdef WPA_TRACE_BFD
10*a1157835SDaniel Fojt #define _GNU_SOURCE
11*a1157835SDaniel Fojt #include <link.h>
12*a1157835SDaniel Fojt #endif /* WPA_TRACE_BCD */
133ff40c12SJohn Marino #include "includes.h"
143ff40c12SJohn Marino
153ff40c12SJohn Marino #include "common.h"
163ff40c12SJohn Marino #include "trace.h"
173ff40c12SJohn Marino
183ff40c12SJohn Marino #ifdef WPA_TRACE
193ff40c12SJohn Marino
203ff40c12SJohn Marino static struct dl_list active_references =
213ff40c12SJohn Marino { &active_references, &active_references };
223ff40c12SJohn Marino
233ff40c12SJohn Marino #ifdef WPA_TRACE_BFD
243ff40c12SJohn Marino #include <bfd.h>
25*a1157835SDaniel Fojt
26*a1157835SDaniel Fojt #define DMGL_PARAMS (1 << 0)
27*a1157835SDaniel Fojt #define DMGL_ANSI (1 << 1)
283ff40c12SJohn Marino
293ff40c12SJohn Marino static char *prg_fname = NULL;
303ff40c12SJohn Marino static bfd *cached_abfd = NULL;
313ff40c12SJohn Marino static asymbol **syms = NULL;
32*a1157835SDaniel Fojt static unsigned long start_offset;
33*a1157835SDaniel Fojt static int start_offset_looked_up;
34*a1157835SDaniel Fojt
35*a1157835SDaniel Fojt
callback(struct dl_phdr_info * info,size_t size,void * data)36*a1157835SDaniel Fojt static int callback(struct dl_phdr_info *info, size_t size, void *data)
37*a1157835SDaniel Fojt {
38*a1157835SDaniel Fojt /*
39*a1157835SDaniel Fojt * dl_iterate_phdr(3):
40*a1157835SDaniel Fojt * "The first object visited by callback is the main program."
41*a1157835SDaniel Fojt */
42*a1157835SDaniel Fojt start_offset = info->dlpi_addr;
43*a1157835SDaniel Fojt
44*a1157835SDaniel Fojt /*
45*a1157835SDaniel Fojt * dl_iterate_phdr(3):
46*a1157835SDaniel Fojt * "The dl_iterate_phdr() function walks through the list of an
47*a1157835SDaniel Fojt * application's shared objects and calls the function callback
48*a1157835SDaniel Fojt * once for each object, until either all shared objects have
49*a1157835SDaniel Fojt * been processed or callback returns a nonzero value."
50*a1157835SDaniel Fojt */
51*a1157835SDaniel Fojt return 1;
52*a1157835SDaniel Fojt }
53*a1157835SDaniel Fojt
543ff40c12SJohn Marino
get_prg_fname(void)553ff40c12SJohn Marino static void get_prg_fname(void)
563ff40c12SJohn Marino {
573ff40c12SJohn Marino char exe[50], fname[512];
583ff40c12SJohn Marino int len;
593ff40c12SJohn Marino os_snprintf(exe, sizeof(exe) - 1, "/proc/%u/exe", getpid());
603ff40c12SJohn Marino len = readlink(exe, fname, sizeof(fname) - 1);
613ff40c12SJohn Marino if (len < 0 || len >= (int) sizeof(fname)) {
62*a1157835SDaniel Fojt wpa_printf(MSG_ERROR, "readlink: %s", strerror(errno));
633ff40c12SJohn Marino return;
643ff40c12SJohn Marino }
653ff40c12SJohn Marino fname[len] = '\0';
663ff40c12SJohn Marino prg_fname = strdup(fname);
673ff40c12SJohn Marino }
683ff40c12SJohn Marino
693ff40c12SJohn Marino
open_bfd(const char * fname)703ff40c12SJohn Marino static bfd * open_bfd(const char *fname)
713ff40c12SJohn Marino {
723ff40c12SJohn Marino bfd *abfd;
733ff40c12SJohn Marino char **matching;
743ff40c12SJohn Marino
753ff40c12SJohn Marino abfd = bfd_openr(prg_fname, NULL);
763ff40c12SJohn Marino if (abfd == NULL) {
773ff40c12SJohn Marino wpa_printf(MSG_INFO, "bfd_openr failed");
783ff40c12SJohn Marino return NULL;
793ff40c12SJohn Marino }
803ff40c12SJohn Marino
813ff40c12SJohn Marino if (bfd_check_format(abfd, bfd_archive)) {
823ff40c12SJohn Marino wpa_printf(MSG_INFO, "bfd_check_format failed");
833ff40c12SJohn Marino bfd_close(abfd);
843ff40c12SJohn Marino return NULL;
853ff40c12SJohn Marino }
863ff40c12SJohn Marino
873ff40c12SJohn Marino if (!bfd_check_format_matches(abfd, bfd_object, &matching)) {
883ff40c12SJohn Marino wpa_printf(MSG_INFO, "bfd_check_format_matches failed");
893ff40c12SJohn Marino free(matching);
903ff40c12SJohn Marino bfd_close(abfd);
913ff40c12SJohn Marino return NULL;
923ff40c12SJohn Marino }
933ff40c12SJohn Marino
943ff40c12SJohn Marino return abfd;
953ff40c12SJohn Marino }
963ff40c12SJohn Marino
973ff40c12SJohn Marino
read_syms(bfd * abfd)983ff40c12SJohn Marino static void read_syms(bfd *abfd)
993ff40c12SJohn Marino {
1003ff40c12SJohn Marino long storage, symcount;
1013ff40c12SJohn Marino bfd_boolean dynamic = FALSE;
1023ff40c12SJohn Marino
1033ff40c12SJohn Marino if (syms)
1043ff40c12SJohn Marino return;
1053ff40c12SJohn Marino
1063ff40c12SJohn Marino if (!(bfd_get_file_flags(abfd) & HAS_SYMS)) {
1073ff40c12SJohn Marino wpa_printf(MSG_INFO, "No symbols");
1083ff40c12SJohn Marino return;
1093ff40c12SJohn Marino }
1103ff40c12SJohn Marino
1113ff40c12SJohn Marino storage = bfd_get_symtab_upper_bound(abfd);
1123ff40c12SJohn Marino if (storage == 0) {
1133ff40c12SJohn Marino storage = bfd_get_dynamic_symtab_upper_bound(abfd);
1143ff40c12SJohn Marino dynamic = TRUE;
1153ff40c12SJohn Marino }
1163ff40c12SJohn Marino if (storage < 0) {
1173ff40c12SJohn Marino wpa_printf(MSG_INFO, "Unknown symtab upper bound");
1183ff40c12SJohn Marino return;
1193ff40c12SJohn Marino }
1203ff40c12SJohn Marino
1213ff40c12SJohn Marino syms = malloc(storage);
1223ff40c12SJohn Marino if (syms == NULL) {
1233ff40c12SJohn Marino wpa_printf(MSG_INFO, "Failed to allocate memory for symtab "
1243ff40c12SJohn Marino "(%ld bytes)", storage);
1253ff40c12SJohn Marino return;
1263ff40c12SJohn Marino }
1273ff40c12SJohn Marino if (dynamic)
1283ff40c12SJohn Marino symcount = bfd_canonicalize_dynamic_symtab(abfd, syms);
1293ff40c12SJohn Marino else
1303ff40c12SJohn Marino symcount = bfd_canonicalize_symtab(abfd, syms);
1313ff40c12SJohn Marino if (symcount < 0) {
1323ff40c12SJohn Marino wpa_printf(MSG_INFO, "Failed to canonicalize %ssymtab",
1333ff40c12SJohn Marino dynamic ? "dynamic " : "");
1343ff40c12SJohn Marino free(syms);
1353ff40c12SJohn Marino syms = NULL;
1363ff40c12SJohn Marino return;
1373ff40c12SJohn Marino }
1383ff40c12SJohn Marino }
1393ff40c12SJohn Marino
1403ff40c12SJohn Marino
1413ff40c12SJohn Marino struct bfd_data {
1423ff40c12SJohn Marino bfd_vma pc;
1433ff40c12SJohn Marino bfd_boolean found;
1443ff40c12SJohn Marino const char *filename;
1453ff40c12SJohn Marino const char *function;
1463ff40c12SJohn Marino unsigned int line;
1473ff40c12SJohn Marino };
1483ff40c12SJohn Marino
1493ff40c12SJohn Marino
find_addr_sect(bfd * abfd,asection * section,void * obj)1503ff40c12SJohn Marino static void find_addr_sect(bfd *abfd, asection *section, void *obj)
1513ff40c12SJohn Marino {
1523ff40c12SJohn Marino struct bfd_data *data = obj;
1533ff40c12SJohn Marino bfd_vma vma;
1543ff40c12SJohn Marino bfd_size_type size;
1553ff40c12SJohn Marino
1563ff40c12SJohn Marino if (data->found)
1573ff40c12SJohn Marino return;
1583ff40c12SJohn Marino
1593ff40c12SJohn Marino if (!(bfd_get_section_vma(abfd, section)))
1603ff40c12SJohn Marino return;
1613ff40c12SJohn Marino
1623ff40c12SJohn Marino vma = bfd_get_section_vma(abfd, section);
1633ff40c12SJohn Marino if (data->pc < vma)
1643ff40c12SJohn Marino return;
1653ff40c12SJohn Marino
1663ff40c12SJohn Marino size = bfd_get_section_size(section);
1673ff40c12SJohn Marino if (data->pc >= vma + size)
1683ff40c12SJohn Marino return;
1693ff40c12SJohn Marino
1703ff40c12SJohn Marino data->found = bfd_find_nearest_line(abfd, section, syms,
1713ff40c12SJohn Marino data->pc - vma,
1723ff40c12SJohn Marino &data->filename,
1733ff40c12SJohn Marino &data->function,
1743ff40c12SJohn Marino &data->line);
1753ff40c12SJohn Marino }
1763ff40c12SJohn Marino
1773ff40c12SJohn Marino
wpa_trace_bfd_addr(void * pc)1783ff40c12SJohn Marino static void wpa_trace_bfd_addr(void *pc)
1793ff40c12SJohn Marino {
1803ff40c12SJohn Marino bfd *abfd = cached_abfd;
1813ff40c12SJohn Marino struct bfd_data data;
1823ff40c12SJohn Marino const char *name;
1833ff40c12SJohn Marino char *aname = NULL;
1843ff40c12SJohn Marino const char *filename;
1853ff40c12SJohn Marino
1863ff40c12SJohn Marino if (abfd == NULL)
1873ff40c12SJohn Marino return;
1883ff40c12SJohn Marino
189*a1157835SDaniel Fojt data.pc = (bfd_hostptr_t) ((u8 *) pc - start_offset);
1903ff40c12SJohn Marino data.found = FALSE;
1913ff40c12SJohn Marino bfd_map_over_sections(abfd, find_addr_sect, &data);
1923ff40c12SJohn Marino
1933ff40c12SJohn Marino if (!data.found)
1943ff40c12SJohn Marino return;
1953ff40c12SJohn Marino
1963ff40c12SJohn Marino do {
1973ff40c12SJohn Marino if (data.function)
1983ff40c12SJohn Marino aname = bfd_demangle(abfd, data.function,
1993ff40c12SJohn Marino DMGL_ANSI | DMGL_PARAMS);
2003ff40c12SJohn Marino name = aname ? aname : data.function;
2013ff40c12SJohn Marino filename = data.filename;
2023ff40c12SJohn Marino if (filename) {
2033ff40c12SJohn Marino char *end = os_strrchr(filename, '/');
2043ff40c12SJohn Marino int i = 0;
2053ff40c12SJohn Marino while (*filename && *filename == prg_fname[i] &&
2063ff40c12SJohn Marino filename <= end) {
2073ff40c12SJohn Marino filename++;
2083ff40c12SJohn Marino i++;
2093ff40c12SJohn Marino }
2103ff40c12SJohn Marino }
2113ff40c12SJohn Marino wpa_printf(MSG_INFO, " %s() %s:%u",
2123ff40c12SJohn Marino name, filename, data.line);
2133ff40c12SJohn Marino free(aname);
214*a1157835SDaniel Fojt aname = NULL;
2153ff40c12SJohn Marino
2163ff40c12SJohn Marino data.found = bfd_find_inliner_info(abfd, &data.filename,
2173ff40c12SJohn Marino &data.function, &data.line);
2183ff40c12SJohn Marino } while (data.found);
2193ff40c12SJohn Marino }
2203ff40c12SJohn Marino
2213ff40c12SJohn Marino
wpa_trace_bfd_addr2func(void * pc)2223ff40c12SJohn Marino static const char * wpa_trace_bfd_addr2func(void *pc)
2233ff40c12SJohn Marino {
2243ff40c12SJohn Marino bfd *abfd = cached_abfd;
2253ff40c12SJohn Marino struct bfd_data data;
2263ff40c12SJohn Marino
2273ff40c12SJohn Marino if (abfd == NULL)
2283ff40c12SJohn Marino return NULL;
2293ff40c12SJohn Marino
230*a1157835SDaniel Fojt data.pc = (bfd_hostptr_t) ((u8 *) pc - start_offset);
2313ff40c12SJohn Marino data.found = FALSE;
2323ff40c12SJohn Marino bfd_map_over_sections(abfd, find_addr_sect, &data);
2333ff40c12SJohn Marino
2343ff40c12SJohn Marino if (!data.found)
2353ff40c12SJohn Marino return NULL;
2363ff40c12SJohn Marino
2373ff40c12SJohn Marino return data.function;
2383ff40c12SJohn Marino }
2393ff40c12SJohn Marino
2403ff40c12SJohn Marino
wpa_trace_bfd_init(void)2413ff40c12SJohn Marino static void wpa_trace_bfd_init(void)
2423ff40c12SJohn Marino {
2433ff40c12SJohn Marino if (!prg_fname) {
2443ff40c12SJohn Marino get_prg_fname();
2453ff40c12SJohn Marino if (!prg_fname)
2463ff40c12SJohn Marino return;
2473ff40c12SJohn Marino }
2483ff40c12SJohn Marino
2493ff40c12SJohn Marino if (!cached_abfd) {
2503ff40c12SJohn Marino cached_abfd = open_bfd(prg_fname);
2513ff40c12SJohn Marino if (!cached_abfd) {
2523ff40c12SJohn Marino wpa_printf(MSG_INFO, "Failed to open bfd");
2533ff40c12SJohn Marino return;
2543ff40c12SJohn Marino }
2553ff40c12SJohn Marino }
2563ff40c12SJohn Marino
2573ff40c12SJohn Marino read_syms(cached_abfd);
2583ff40c12SJohn Marino if (!syms) {
2593ff40c12SJohn Marino wpa_printf(MSG_INFO, "Failed to read symbols");
2603ff40c12SJohn Marino return;
2613ff40c12SJohn Marino }
262*a1157835SDaniel Fojt
263*a1157835SDaniel Fojt if (!start_offset_looked_up) {
264*a1157835SDaniel Fojt dl_iterate_phdr(callback, NULL);
265*a1157835SDaniel Fojt start_offset_looked_up = 1;
266*a1157835SDaniel Fojt }
2673ff40c12SJohn Marino }
2683ff40c12SJohn Marino
2693ff40c12SJohn Marino
wpa_trace_dump_funcname(const char * title,void * pc)2703ff40c12SJohn Marino void wpa_trace_dump_funcname(const char *title, void *pc)
2713ff40c12SJohn Marino {
2723ff40c12SJohn Marino wpa_printf(MSG_INFO, "WPA_TRACE: %s: %p", title, pc);
2733ff40c12SJohn Marino wpa_trace_bfd_init();
2743ff40c12SJohn Marino wpa_trace_bfd_addr(pc);
2753ff40c12SJohn Marino }
2763ff40c12SJohn Marino
277*a1157835SDaniel Fojt
wpa_trace_calling_func(const char * buf[],size_t len)278*a1157835SDaniel Fojt size_t wpa_trace_calling_func(const char *buf[], size_t len)
279*a1157835SDaniel Fojt {
280*a1157835SDaniel Fojt bfd *abfd;
281*a1157835SDaniel Fojt void *btrace_res[WPA_TRACE_LEN];
282*a1157835SDaniel Fojt int i, btrace_num;
283*a1157835SDaniel Fojt size_t pos = 0;
284*a1157835SDaniel Fojt
285*a1157835SDaniel Fojt if (len == 0)
286*a1157835SDaniel Fojt return 0;
287*a1157835SDaniel Fojt if (len > WPA_TRACE_LEN)
288*a1157835SDaniel Fojt len = WPA_TRACE_LEN;
289*a1157835SDaniel Fojt
290*a1157835SDaniel Fojt wpa_trace_bfd_init();
291*a1157835SDaniel Fojt abfd = cached_abfd;
292*a1157835SDaniel Fojt if (!abfd)
293*a1157835SDaniel Fojt return 0;
294*a1157835SDaniel Fojt
295*a1157835SDaniel Fojt btrace_num = backtrace(btrace_res, len);
296*a1157835SDaniel Fojt if (btrace_num < 1)
297*a1157835SDaniel Fojt return 0;
298*a1157835SDaniel Fojt
299*a1157835SDaniel Fojt for (i = 0; i < btrace_num; i++) {
300*a1157835SDaniel Fojt struct bfd_data data;
301*a1157835SDaniel Fojt
302*a1157835SDaniel Fojt data.pc = (bfd_hostptr_t) ((u8 *) btrace_res[i] - start_offset);
303*a1157835SDaniel Fojt data.found = FALSE;
304*a1157835SDaniel Fojt bfd_map_over_sections(abfd, find_addr_sect, &data);
305*a1157835SDaniel Fojt
306*a1157835SDaniel Fojt while (data.found) {
307*a1157835SDaniel Fojt if (data.function &&
308*a1157835SDaniel Fojt (pos > 0 ||
309*a1157835SDaniel Fojt os_strcmp(data.function, __func__) != 0)) {
310*a1157835SDaniel Fojt buf[pos++] = data.function;
311*a1157835SDaniel Fojt if (pos == len)
312*a1157835SDaniel Fojt return pos;
313*a1157835SDaniel Fojt }
314*a1157835SDaniel Fojt
315*a1157835SDaniel Fojt data.found = bfd_find_inliner_info(abfd, &data.filename,
316*a1157835SDaniel Fojt &data.function,
317*a1157835SDaniel Fojt &data.line);
318*a1157835SDaniel Fojt }
319*a1157835SDaniel Fojt }
320*a1157835SDaniel Fojt
321*a1157835SDaniel Fojt return pos;
322*a1157835SDaniel Fojt }
323*a1157835SDaniel Fojt
3243ff40c12SJohn Marino #else /* WPA_TRACE_BFD */
3253ff40c12SJohn Marino
3263ff40c12SJohn Marino #define wpa_trace_bfd_init() do { } while (0)
3273ff40c12SJohn Marino #define wpa_trace_bfd_addr(pc) do { } while (0)
3283ff40c12SJohn Marino #define wpa_trace_bfd_addr2func(pc) NULL
3293ff40c12SJohn Marino
3303ff40c12SJohn Marino #endif /* WPA_TRACE_BFD */
3313ff40c12SJohn Marino
wpa_trace_dump_func(const char * title,void ** btrace,int btrace_num)3323ff40c12SJohn Marino void wpa_trace_dump_func(const char *title, void **btrace, int btrace_num)
3333ff40c12SJohn Marino {
3343ff40c12SJohn Marino char **sym;
3353ff40c12SJohn Marino int i;
3363ff40c12SJohn Marino enum { TRACE_HEAD, TRACE_RELEVANT, TRACE_TAIL } state;
3373ff40c12SJohn Marino
3383ff40c12SJohn Marino wpa_trace_bfd_init();
3393ff40c12SJohn Marino wpa_printf(MSG_INFO, "WPA_TRACE: %s - START", title);
3403ff40c12SJohn Marino sym = backtrace_symbols(btrace, btrace_num);
3413ff40c12SJohn Marino state = TRACE_HEAD;
3423ff40c12SJohn Marino for (i = 0; i < btrace_num; i++) {
3433ff40c12SJohn Marino const char *func = wpa_trace_bfd_addr2func(btrace[i]);
3443ff40c12SJohn Marino if (state == TRACE_HEAD && func &&
3453ff40c12SJohn Marino (os_strcmp(func, "wpa_trace_add_ref_func") == 0 ||
3463ff40c12SJohn Marino os_strcmp(func, "wpa_trace_check_ref") == 0 ||
3473ff40c12SJohn Marino os_strcmp(func, "wpa_trace_show") == 0))
3483ff40c12SJohn Marino continue;
3493ff40c12SJohn Marino if (state == TRACE_TAIL && sym && sym[i] &&
3503ff40c12SJohn Marino os_strstr(sym[i], "__libc_start_main"))
3513ff40c12SJohn Marino break;
3523ff40c12SJohn Marino if (state == TRACE_HEAD)
3533ff40c12SJohn Marino state = TRACE_RELEVANT;
3543ff40c12SJohn Marino if (sym)
3553ff40c12SJohn Marino wpa_printf(MSG_INFO, "[%d]: %s", i, sym[i]);
3563ff40c12SJohn Marino else
3573ff40c12SJohn Marino wpa_printf(MSG_INFO, "[%d]: ?? [%p]", i, btrace[i]);
3583ff40c12SJohn Marino wpa_trace_bfd_addr(btrace[i]);
3593ff40c12SJohn Marino if (state == TRACE_RELEVANT && func &&
3603ff40c12SJohn Marino os_strcmp(func, "main") == 0)
3613ff40c12SJohn Marino state = TRACE_TAIL;
3623ff40c12SJohn Marino }
3633ff40c12SJohn Marino free(sym);
3643ff40c12SJohn Marino wpa_printf(MSG_INFO, "WPA_TRACE: %s - END", title);
3653ff40c12SJohn Marino }
3663ff40c12SJohn Marino
3673ff40c12SJohn Marino
wpa_trace_show(const char * title)3683ff40c12SJohn Marino void wpa_trace_show(const char *title)
3693ff40c12SJohn Marino {
3703ff40c12SJohn Marino struct info {
3713ff40c12SJohn Marino WPA_TRACE_INFO
3723ff40c12SJohn Marino } info;
3733ff40c12SJohn Marino wpa_trace_record(&info);
3743ff40c12SJohn Marino wpa_trace_dump(title, &info);
3753ff40c12SJohn Marino }
3763ff40c12SJohn Marino
3773ff40c12SJohn Marino
wpa_trace_add_ref_func(struct wpa_trace_ref * ref,const void * addr)3783ff40c12SJohn Marino void wpa_trace_add_ref_func(struct wpa_trace_ref *ref, const void *addr)
3793ff40c12SJohn Marino {
3803ff40c12SJohn Marino if (addr == NULL)
3813ff40c12SJohn Marino return;
3823ff40c12SJohn Marino ref->addr = addr;
3833ff40c12SJohn Marino wpa_trace_record(ref);
3843ff40c12SJohn Marino dl_list_add(&active_references, &ref->list);
3853ff40c12SJohn Marino }
3863ff40c12SJohn Marino
3873ff40c12SJohn Marino
wpa_trace_check_ref(const void * addr)3883ff40c12SJohn Marino void wpa_trace_check_ref(const void *addr)
3893ff40c12SJohn Marino {
3903ff40c12SJohn Marino struct wpa_trace_ref *ref;
3913ff40c12SJohn Marino dl_list_for_each(ref, &active_references, struct wpa_trace_ref, list) {
3923ff40c12SJohn Marino if (addr != ref->addr)
3933ff40c12SJohn Marino continue;
3943ff40c12SJohn Marino wpa_trace_show("Freeing referenced memory");
3953ff40c12SJohn Marino wpa_trace_dump("Reference registration", ref);
3963ff40c12SJohn Marino abort();
3973ff40c12SJohn Marino }
3983ff40c12SJohn Marino }
3993ff40c12SJohn Marino
400*a1157835SDaniel Fojt
wpa_trace_deinit(void)401*a1157835SDaniel Fojt void wpa_trace_deinit(void)
402*a1157835SDaniel Fojt {
403*a1157835SDaniel Fojt #ifdef WPA_TRACE_BFD
404*a1157835SDaniel Fojt free(syms);
405*a1157835SDaniel Fojt syms = NULL;
406*a1157835SDaniel Fojt #endif /* WPA_TRACE_BFD */
407*a1157835SDaniel Fojt }
408*a1157835SDaniel Fojt
4093ff40c12SJohn Marino #endif /* WPA_TRACE */
410