| /* |
| * Copyright (c) 2006 Atheros Communications Inc. |
| * All rights reserved. |
| * |
| * |
| * |
| // |
| // Permission to use, copy, modify, and/or distribute this software for any |
| // purpose with or without fee is hereby granted, provided that the above |
| // copyright notice and this permission notice appear in all copies. |
| // |
| // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
| // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
| // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
| // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
| // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
| // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
| // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
| // |
| // |
| * |
| */ |
| |
| /* This tool parses the recevent logs stored in the binary format |
| by the wince athsrc */ |
| |
| #include <stdlib.h> |
| #include <stdio.h> |
| #include <unistd.h> |
| #include <string.h> |
| #include <errno.h> |
| #include <getopt.h> |
| #include <time.h> |
| #include <asm/types.h> |
| #include <sys/types.h> |
| #include <sys/ioctl.h> |
| #include <sys/socket.h> |
| #include <linux/netlink.h> |
| #include <linux/rtnetlink.h> |
| #include <linux/types.h> |
| #include <linux/if.h> |
| #include <linux/wireless.h> |
| #include <a_config.h> |
| #include <a_osapi.h> |
| #include <a_types.h> |
| #include <athdefs.h> |
| #include <ieee80211.h> |
| #include <wmi.h> |
| #include <athdrv_linux.h> |
| #include <dbglog_api.h> |
| |
| #undef DEBUG |
| #undef DBGLOG_DEBUG |
| |
| #define ID_LEN 2 |
| #define FILENAME_LENGTH_MAX 128 |
| #define DBGLOG_FILE "dbglog.h" |
| #define DBGLOGID_FILE "dbglog_id.h" |
| #define DBGLOG_OUTPUT_FILE "dbglog.out" |
| |
| const A_CHAR *progname; |
| A_CHAR dbglogfile[FILENAME_LENGTH_MAX]; |
| A_CHAR dbglogidfile[FILENAME_LENGTH_MAX]; |
| A_CHAR dbglogoutfile[FILENAME_LENGTH_MAX]; |
| A_CHAR dbgloginfile[FILENAME_LENGTH_MAX]; |
| FILE *fpout; |
| FILE *fpin; |
| int fmtlv; |
| int outfmt; /* 0: raw, 1: html 2: RTF */ |
| int headerlen; |
| int noSystime; |
| |
| A_CHAR dbglog_id_tag[DBGLOG_MODULEID_NUM_MAX][DBGLOG_DBGID_NUM_MAX][DBGLOG_DBGID_DEFINITION_LEN_MAX]; |
| |
| #define AR6K_MAX_DBG_BUFFER_SIZE 1500 |
| |
| struct dbg_binary_record { |
| A_UINT32 ts; |
| A_UINT32 length; |
| A_UINT8 log[AR6K_MAX_DBG_BUFFER_SIZE]; |
| }; |
| |
| struct dbg_binary_header { |
| A_UINT8 sig; |
| A_UINT8 ver; |
| A_UINT16 len; |
| A_UINT32 reserved; |
| }; |
| |
| |
| #ifdef DEBUG |
| A_INT32 debugRecEvent = 0; |
| #define RECEVENT_DEBUG_PRINTF(args...) if (debugRecEvent) printf(args); |
| #else |
| #define RECEVENT_DEBUG_PRINTF(args...) |
| #endif |
| |
| static const A_CHAR htmlHeader[] = "<br/><style type='text/css'>\ |
| .m0 { color:#ff0000; }\ |
| .m1 { color:#0000FF; }\ |
| .m2 { color:#8d8d8d; }\ |
| .m3 { color:#000000; }\ |
| .m4 { color:#a11693; }\ |
| .m5 { color:#b452ab; }\ |
| .m6 { color:#d18eca; }\ |
| .m7 { color:#ff3205; }\ |
| .m8 { color:#ff2428; }\ |
| .m9 { color:#d700b4; }\ |
| .m10 { color:#ffe20e; }\ |
| .m11 { color:#ff0072; }\ |
| .m12 { color:#00a700; }\ |
| .m13 { color:#437197; }\ |
| .m14 { color:#e64248; }\ |
| .m15 { color:#5a1484; }\ |
| </style>\n"; |
| |
| static const A_CHAR htmlFooter[] = ""; |
| |
| static const A_CHAR rtfHeader[] = "{\\rtf1\\ansi\\deff0{\\fonttbl\\f0\\fswiss Helvetica;}\n\ |
| {\\colortbl \\red255\\green0\\blue0;\ |
| \\red0\\green0\\blue255;\ |
| \\red141\\green141\\blue141;\ |
| \\red0\\green0\\blue0;\ |
| \\red161\\green22\\blue147;\ |
| \\red180\\green82\\blue171;\ |
| \\red209\\green142\\blue202;\ |
| \\red255\\green50\\blue5;\ |
| \\red255\\green36\\blue40;\ |
| \\red215\\green0\\blue180;\ |
| \\red255\\green226\\blue14;\ |
| \\red255\\green0\\blue114;\ |
| \\red0\\green167\\blue0;\ |
| \\red67\\green113\\blue151;\ |
| \\red230\\green66\\blue72;\ |
| \\red90\\green20\\blue132;\ |
| }\\f0\\cf3 "; |
| |
| static const A_CHAR rtfFooter[] = "}"; |
| |
| A_INT32 |
| string_search(FILE *fp, A_CHAR *string) |
| { |
| A_CHAR str[DBGLOG_DBGID_DEFINITION_LEN_MAX]; |
| |
| rewind(fp); |
| memset(str, 0, DBGLOG_DBGID_DEFINITION_LEN_MAX); |
| while (!feof(fp)) { |
| if (fgets(str, sizeof(str), fp)) { |
| if (strstr(str, string)) return 1; |
| } |
| } |
| |
| return 0; |
| } |
| |
| void |
| get_module_name(A_CHAR *string, A_CHAR *dest) |
| { |
| A_CHAR *str1, *str2; |
| A_CHAR str[DBGLOG_DBGID_DEFINITION_LEN_MAX]; |
| |
| memset(str, 0, DBGLOG_DBGID_DEFINITION_LEN_MAX); |
| strcpy(str, string); |
| str1 = strtok(str, "_"); |
| while ((str2 = strtok(NULL, "_"))) { |
| str1 = str2; |
| } |
| |
| strcpy(dest, str1); |
| } |
| |
| #ifdef DBGLOG_DEBUG |
| void |
| dbglog_print_id_tags(void) |
| { |
| A_INT32 i, j; |
| |
| for (i = 0; i < DBGLOG_MODULEID_NUM_MAX; i++) { |
| for (j = 0; j < DBGLOG_DBGID_NUM_MAX; j++) { |
| printf("[%d][%d]: %s\n", i, j, dbglog_id_tag[i][j]); |
| } |
| } |
| } |
| #endif /* DBGLOG_DEBUG */ |
| |
| A_INT32 |
| dbglog_generate_id_tags(void) |
| { |
| A_INT32 id1, id2; |
| FILE *fp1, *fp2; |
| A_CHAR str1[DBGLOG_DBGID_DEFINITION_LEN_MAX]; |
| A_CHAR str2[DBGLOG_DBGID_DEFINITION_LEN_MAX]; |
| A_CHAR str3[DBGLOG_DBGID_DEFINITION_LEN_MAX]; |
| |
| if (!(fp1 = fopen(dbglogfile, "r"))) { |
| perror(dbglogfile); |
| return -1; |
| } |
| |
| if (!(fp2 = fopen(dbglogidfile, "r"))) { |
| fclose(fp1); |
| perror(dbglogidfile); |
| return -1; |
| } |
| |
| memset(dbglog_id_tag, 0, sizeof(dbglog_id_tag)); |
| if (string_search(fp1, "DBGLOG_MODULEID_START")) { |
| int ret = fscanf(fp1, "%s %s %d", str1, str2, &id1); |
| do { |
| memset(str3, 0, DBGLOG_DBGID_DEFINITION_LEN_MAX); |
| get_module_name(str2, str3); |
| strcat(str3, "_DBGID_DEFINITION_START"); |
| if (string_search(fp2, str3)) { |
| memset(str3, 0, DBGLOG_DBGID_DEFINITION_LEN_MAX); |
| get_module_name(str2, str3); |
| strcat(str3, "_DBGID_DEFINITION_END"); |
| ret = fscanf(fp2, "%s %s %d", str1, str2, &id2); |
| while (!(strstr(str2, str3))) { |
| strcpy((A_CHAR *)&dbglog_id_tag[id1][id2], str2); |
| ret= fscanf(fp2, "%s %s %d", str1, str2, &id2); |
| } |
| } |
| ret = fscanf(fp1, "%s %s %d", str1, str2, &id1); |
| } while (!(strstr(str2, "DBGLOG_MODULEID_END"))); |
| } |
| |
| fclose(fp2); |
| fclose(fp1); |
| |
| return 0; |
| } |
| |
| static void |
| usage(void) |
| { |
| const char options[] = |
| "Options:\n\ |
| -d, --srcdir=<Directory containing the dbglog header files> [Optional]\n\ |
| -f, --fileformat 0:plain text, 1:HTML, 2:RTF \n\ |
| -s, --nosystime Don't print out system time\n\ |
| -t, --format 0:Raw, 1:brief, 2:full [0 by default]\n"; |
| |
| fprintf(stderr, "usage:\n%s [options] <input log file> <output file>\n", progname); |
| fprintf(stderr, "%s", options); |
| exit(-1); |
| } |
| |
| static A_INT32 |
| decode_debug_rec(struct dbg_binary_record *dbg_rec) |
| { |
| #define BUF_SIZE 512 |
| A_UINT32 count; |
| A_UINT32 numargs; |
| A_INT32 *buffer; |
| A_UINT32 length; |
| A_CHAR buf[BUF_SIZE]; |
| A_UINT32 curpos; |
| static A_INT32 numOfRec = 0; |
| A_INT32 len; |
| char outputBuf[2048]; |
| |
| #ifdef DBGLOG_DEBUG |
| RECEVENT_DEBUG_PRINTF("Application received target debug event: %d\n", len); |
| #endif /* DBGLOG_DEBUG */ |
| count = 0; |
| len = dbg_rec->length; |
| length = (len >> 2); |
| buffer = (A_INT32 *)dbg_rec->log; |
| |
| while (count < length) { |
| A_INT32 lret, oret; |
| A_UINT32 moduleid = DBGLOG_GET_MODULEID(buffer[count]); |
| numargs = DBGLOG_GET_NUMARGS(buffer[count]); |
| oret = 0; |
| if (outfmt==1) { |
| oret += snprintf(outputBuf+oret, sizeof(outputBuf)-oret, |
| "<span class='m%d'>", moduleid); |
| } else if (outfmt==2) { |
| oret += snprintf(outputBuf+oret, sizeof(outputBuf)-oret, |
| "\\cf%d ", moduleid); |
| } |
| lret=dbg_formater(fmtlv, outputBuf+oret, sizeof(outputBuf)-oret, |
| noSystime ? 0 : dbg_rec->ts, &buffer[count]); |
| if ( lret > 0) { |
| #ifdef DBGLOG_DEBUG |
| RECEVENT_DEBUG_PRINTF("%s", outputBuf+oret); |
| #endif /* DBGLOG_DEBUG */ |
| |
| oret += lret; |
| if (outfmt==1) { |
| oret += snprintf(outputBuf+oret, sizeof(outputBuf)-oret, "</span><br/>"); |
| } else if (outfmt==2) { |
| oret += snprintf(outputBuf+oret, sizeof(outputBuf)-oret, "\\par"); |
| } |
| fprintf(fpout, "%s", outputBuf); |
| } |
| count += (numargs + 1); |
| |
| numOfRec++; |
| } |
| |
| /* Update the last rec at the top of file */ |
| curpos = ftell(fpout); |
| if( fgets(buf, BUF_SIZE, fpout) ) { |
| buf[BUF_SIZE - 1] = 0; /* In case string is longer from logs */ |
| length = strlen(buf); |
| memset(buf, ' ', length-1); |
| buf[length] = 0; |
| fseek(fpout, curpos, SEEK_SET); |
| fprintf(fpout, "%s", buf); |
| } |
| |
| rewind(fpout); |
| /* Update last record */ |
| fseek(fpout, headerlen, SEEK_SET); |
| fprintf(fpout, "%08d\n", numOfRec); |
| fseek(fpout, curpos, SEEK_SET); |
| fflush(fpout); |
| |
| #undef BUF_SIZE |
| return 0; |
| } |
| |
| A_INT32 main(A_INT32 argc, A_CHAR** argv) |
| { |
| A_CHAR *workarea = NULL; |
| struct dbg_binary_record dbg_rec; |
| A_UINT32 min_ts; |
| A_INT32 min_rec_num; |
| A_UINT32 rec_num; |
| A_INT32 i; |
| struct dbg_binary_header dbg_header; |
| A_UINT16 dbg_rec_len; |
| A_UINT16 dbg_header_len; |
| |
| progname = argv[0]; |
| memset(dbgloginfile, 0, FILENAME_LENGTH_MAX); |
| memset(dbglogoutfile, 0, FILENAME_LENGTH_MAX); |
| |
| while (1) { |
| int option_index = 0; |
| int c; |
| static struct option long_options[] = { |
| {"srcdir", 1, NULL, 'd'}, |
| {"format", 1, NULL, 't'}, |
| {"fileformat", 1, NULL, 'f'}, |
| {"nosystime", 0, NULL, 's'}, |
| {0, 0, 0, 0} |
| }; |
| c = getopt_long (argc, argv, "d:t:f:s", long_options, &option_index); |
| if (c == -1) break; |
| |
| switch (c) { |
| case 'd': |
| workarea = optarg; |
| break; |
| case 't': |
| fmtlv = atoi(optarg); |
| break; |
| case 'f': |
| outfmt = atoi(optarg); |
| break; |
| case 's': |
| noSystime = 1; |
| break; |
| default: |
| usage(); |
| } |
| } |
| |
| if (!workarea) { |
| workarea = getenv("WORKAREA"); |
| } |
| |
| if (workarea == NULL) { |
| printf("export WORKAREA or use -d option\n"); |
| return -1; |
| } |
| if ((argc - optind)<2) { |
| usage(); |
| return -1; |
| } |
| /* Get the file name for dbglog header file */ |
| memset(dbglogfile, 0, FILENAME_LENGTH_MAX); |
| strcpy(dbglogfile, workarea); |
| strcat(dbglogfile, "/include/"); |
| strcat(dbglogfile, DBGLOG_FILE); |
| |
| /* Get the file name for dbglog id header file */ |
| memset(dbglogidfile, 0, FILENAME_LENGTH_MAX); |
| strcpy(dbglogidfile, workarea); |
| strcat(dbglogidfile, "/include/"); |
| strcat(dbglogidfile, DBGLOGID_FILE); |
| |
| /* Get the file name for dbglog input file */ |
| memset(dbgloginfile, 0, FILENAME_LENGTH_MAX); |
| strcpy(dbgloginfile, argv[optind]); |
| if (!(fpin = fopen(dbgloginfile, "rb"))) { |
| perror(dbgloginfile); |
| return -1; |
| } |
| |
| /* Get the file name for dbglog output file */ |
| memset(dbglogoutfile, 0, FILENAME_LENGTH_MAX); |
| strcpy(dbglogoutfile, argv[optind+1]); |
| if (!(fpout = fopen(dbglogoutfile, "w+"))) { |
| perror(dbglogoutfile); |
| return -1; |
| } |
| |
| |
| /* first 8 bytes are to indicate the last record */ |
| switch (outfmt) { |
| case 1: |
| headerlen = strlen(htmlHeader); |
| break; |
| case 2: |
| headerlen = strlen(rtfHeader); |
| break; |
| default: |
| headerlen = 0; |
| break; |
| } |
| if (outfmt==1) { |
| fprintf(fpout, "%s", htmlHeader); |
| } else if (outfmt==2) { |
| fprintf(fpout, "%s", rtfHeader); |
| } |
| fseek(fpout, headerlen + 8, SEEK_SET); |
| fprintf(fpout, "\n"); |
| |
| dbglog_generate_id_tags(); |
| #ifdef DBGLOG_DEBUG |
| dbglog_print_id_tags(); |
| #endif /* DBGLOG_DEBUG */ |
| |
| /* first 8 bytes are header */ |
| if (fread(&dbg_header, sizeof(struct dbg_binary_header), 1, fpin)!=1) { |
| perror("dbg_header mismatch\n"); |
| return -1; |
| } |
| |
| /* check header signature */ |
| dbg_rec_len = sizeof(A_UINT32) * 2; |
| if (dbg_header.sig == 0xDB) { |
| dbg_rec_len += dbg_header.len; |
| dbg_header_len = 8; |
| } else { |
| /* header not present; assume max size */ |
| dbg_rec_len += AR6K_MAX_DBG_BUFFER_SIZE; |
| dbg_header_len = 0; |
| } |
| |
| /* go past header */ |
| fseek(fpin, dbg_header_len , SEEK_SET); |
| |
| min_ts = 0xFFFFFFFF; |
| rec_num = 0; |
| min_rec_num = 0; |
| while (!feof(fpin)) { |
| if (fread (&dbg_rec, dbg_rec_len, 1, fpin)) { |
| if (dbg_rec.ts < min_ts) { |
| min_ts = dbg_rec.ts; |
| min_rec_num = rec_num; |
| } |
| rec_num++; |
| } |
| } |
| |
| /* go past header */ |
| fseek(fpin, dbg_header_len , SEEK_SET); |
| |
| // Goto the first min record |
| fseek(fpin, min_rec_num * dbg_rec_len , SEEK_CUR); |
| while (!feof(fpin)) { |
| if (fread (&dbg_rec, dbg_rec_len, 1, fpin)) { |
| decode_debug_rec(&dbg_rec); |
| } |
| } |
| |
| /* go past header */ |
| fseek(fpin, dbg_header_len , SEEK_SET); |
| |
| for (i=0;i<min_rec_num;i++) { |
| if (fread (&dbg_rec, dbg_rec_len, 1, fpin)) { |
| decode_debug_rec(&dbg_rec); |
| } else { |
| break; |
| } |
| } |
| |
| if (outfmt==1) { |
| fprintf(fpout, "%s", htmlFooter); |
| } else if (outfmt==2) { |
| fprintf(fpout, "%s", rtfFooter); |
| } |
| |
| fclose(fpin); |
| fclose(fpout); |
| return 0; |
| } |
| |