diff options
author | Ivan Mikhaylov <ivan@de.ibm.com> | 2016-09-02 14:42:59 +0300 |
---|---|---|
committer | Ivan Mikhaylov <ivan@de.ibm.com> | 2016-09-19 14:35:37 +0300 |
commit | c15e2267288e26a04f168a3c01f2b09f50f267bf (patch) | |
tree | cc8f5e51a0ccd1b8cadbedacc604e48d8a47c079 | |
download | fsp-trace-c15e2267288e26a04f168a3c01f2b09f50f267bf.tar.gz fsp-trace-c15e2267288e26a04f168a3c01f2b09f50f267bf.zip |
initial fsp-trace commit
-rw-r--r-- | LICENSE | 175 | ||||
-rw-r--r-- | Makefile | 11 | ||||
-rw-r--r-- | README.md | 14 | ||||
-rw-r--r-- | adal_common.c | 90 | ||||
-rw-r--r-- | adal_common.h | 169 | ||||
-rw-r--r-- | adal_parse.c | 2693 | ||||
-rw-r--r-- | adal_trace.c | 870 | ||||
-rw-r--r-- | copyright.c | 23 | ||||
-rw-r--r-- | fsp-trace.c | 1247 | ||||
-rw-r--r-- | jhash.h | 765 | ||||
-rw-r--r-- | trace_adal.c | 970 | ||||
-rw-r--r-- | trace_adal.h | 872 |
12 files changed, 7899 insertions, 0 deletions
@@ -0,0 +1,175 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..1fca1c3 --- /dev/null +++ b/Makefile @@ -0,0 +1,11 @@ +ARCH = $(shell gcc -dumpmachine) +CFLAGS = -fPIC -fno-strict-aliasing -W -Wall -Wextra -Wformat -Wno-uninitialized -pipe -g -O2 -DARCH=\"$(ARCH)\" -pthread +TARGET = fsp-trace + +all: $(TARGET) + +$(TARGET): fsp-trace.c copyright.c adal_common.c adal_trace.c adal_parse.c + $(CC) $^ $(CFLAGS) -o $@ + +clean distclean: + $(RM) $(TARGET) diff --git a/README.md b/README.md new file mode 100644 index 0000000..6bbc699 --- /dev/null +++ b/README.md @@ -0,0 +1,14 @@ +# fsp-trace # +Parser utility for binary traces from fsp-trace driver. + +## HOWTO ## +For using it you have to find proper hash file/trex string file +which is needed to associate hashs with strings. + +Example: + + fsp-trace -s hash_file.trc <buffer_file> + +## Building ## + + make diff --git a/adal_common.c b/adal_common.c new file mode 100644 index 0000000..5c1b71e --- /dev/null +++ b/adal_common.c @@ -0,0 +1,90 @@ +/* */ +/* OpenPOWER fsp-trace Project */ +/* Contributors Listed Below - COPYRIGHT 2004,2012 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ + +#include <ctype.h> +#include <stdint.h> +#include <stdio.h> +#include <string.h> + +#include <sys/types.h> +typedef u_int32_t u32 ; +typedef u_int8_t u8 ; +#include "jhash.h" + + +#include "trace_adal.h" + +int g_verbose_level = 0; + +static const char copyright[] __attribute__ ((unused)) + __attribute__ ((section(".info"))) = + "Licensed under the Apache License, Version 2.0.\n"; + + +trace_hash_val trace_adal_hash(const char *i_str, const uint32_t i_key) +{ + return hashlittle(i_str,strlen(i_str),i_key); +} + + +// print data dump with template: +// [00000000] 00000000 00000000 00000000 00000000 *................* +void print_dump(const char *src, int len) +{ + +#define LINEDATALEN 16 + unsigned char databuf[LINEDATALEN]; // max len of dump line is at this time 72 chars + unsigned char strbuf[(LINEDATALEN) + 1]; + int i, cnt = 0; + unsigned long realaddr = 0; + + if (len <= 0) { + return; + } + for (cnt = 16; (cnt - 16) < len; realaddr += 16, src += 16, cnt += 16) { + if (cnt > len) { + int b2c = len - (cnt - 16); + + memcpy(databuf, src, b2c); + memcpy(strbuf, src, len - (cnt - 16)); + memset(databuf + (len - (cnt - 16)), 0, (LINEDATALEN) - (len - (cnt - 16))); + memset(strbuf + (len - (cnt - 16)), ' ', + (LINEDATALEN) - (len - (cnt - 16))); + } else { + memset(databuf, 0, LINEDATALEN); + memcpy(databuf, src, LINEDATALEN); + memset(strbuf, 0, (LINEDATALEN)); + memcpy(strbuf, src, LINEDATALEN); + } + strbuf[LINEDATALEN] = 0; + + for (i = 0; i < LINEDATALEN; i++) { // make strbuf printable + if (!isprint(strbuf[i])) { + strbuf[i] = '.'; + } + } + + printf("^[0x%08lx] %02x%02x%02x%02x %02x%02x%02x%02x " + "%02x%02x%02x%02x %02x%02x%02x%02x * %s *\n", + realaddr, databuf[0], databuf[1], databuf[2], databuf[3], + databuf[4], databuf[5], databuf[6], databuf[7], + databuf[8], databuf[9], databuf[10], databuf[11], + databuf[12], databuf[13], databuf[14], databuf[15], strbuf); + } +} diff --git a/adal_common.h b/adal_common.h new file mode 100644 index 0000000..25bf226 --- /dev/null +++ b/adal_common.h @@ -0,0 +1,169 @@ +/* */ +/* OpenPOWER fsp-trace Project */ +/* Contributors Listed Below - COPYRIGHT 2004,2012 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ + +#ifndef _ADAL_COMMON_H +#define _ADAL_COMMON_H + +#include <stdint.h> +#include <inttypes.h> +//#include <sys/types.h> +#include <ctype.h> +#include "trace_adal.h" + +#define TYPE_UINT64 1 +#define TYPE_FLOAT 2 +#define TYPE_STRING 3 + +#define DEFAULT_FMT_SZ 256 +extern char g_time_format[DEFAULT_FMT_SZ]; + +/* + * @brief Time stamp contains seconds and microseconds + */ +#define TRACE_TIME_REAL 0 +/* + * @brief Time stamp contains a 64 bit counter from a 50MHZ cpu + */ +#define TRACE_TIME_50MHZ 1 +/* + * @brief Time stamp contains a 64 bit counter from a 167MHZ cpu (1000/6) + */ +#define TRACE_TIME_167MHZ 3 +/* + * @brief Time stamp contains a 64 bit counter from a 200MHZ cpu + */ +#define TRACE_TIME_200MHZ 2 +#define TRACE_TIME_TIMESPEC 4 +/* + * @brief Time stamp is "Epoch" based (0 equals 1970-01-01 00:00:00 UTC) + */ +#define TRACE_TIME_EPOCH 128 + +#define TRACE_TIME_UNKNOWN 255 + +#define TRACE_TIME_MAX 3 + +/* + * @brief maximum value of arguments to trace_adal_write_all. + */ +#define MAX_ARGS 9 + +/* a trace entry with args + * the args can be accessed via h.args[..] too + */ +struct trace_write_entry { + trace_entry_head_t h; + uint32_t args[9]; +}; +typedef struct trace_write_entry trace_write_entry_t; + +/* struct for SET_DEBUG/CONSOLE/PIPE ioctl calls */ +struct trace_set_config { + union { + const char *name; + trace_desc_t td; + } u; + int newlevel; +}; + +/* trace descriptor with console level + */ +struct trace_set_console { + trace_desc_t td; + uint32_t level; +}; +typedef struct trace_set_console trace_set_console_t; + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * @brief change case to upper + */ +static inline void toupper_string(char *text) +{ + size_t j; + + for (j = 0; j < strlen(text); j++) { + text[j] = toupper(text[j]); + } +} + + +/* + * @brief print to stdout dump of src + */ +void print_dump(const char *src, int len); + +#ifdef __cplusplus +} +#endif + +// verbose types +#define TRACE_TYPE_ERROR 1 // error descriptions +#define TRACE_TYPE_PERROR 2 // error descriptions with perror call +#define TRACE_TYPE_INFO 4 // runtime info +#define TRACE_TYPE_VARS 8 // interpreted data output +#define TRACE_TYPE_FUNC 16 // functions entry/leave +#define TRACE_TYPE_RAW 32 // raw data output +#define TRACE_TYPE_DEBUG 64 // other debug output + +// verbose levels (-v parameter) +#define TRACE_LVL0 0 +#define TRACE_LVL1 TRACE_TYPE_ERROR | TRACE_TYPE_PERROR; +#define TRACE_LVL2 TRACE_TYPE_ERROR | TRACE_TYPE_PERROR | TRACE_TYPE_INFO; +#define TRACE_LVL3 TRACE_TYPE_ERROR | TRACE_TYPE_PERROR | TRACE_TYPE_INFO | TRACE_TYPE_VARS; +#define TRACE_LVL4 TRACE_TYPE_ERROR | TRACE_TYPE_PERROR | TRACE_TYPE_INFO | TRACE_TYPE_VARS | TRACE_TYPE_FUNC; +#define TRACE_LVL5 TRACE_TYPE_ERROR | TRACE_TYPE_PERROR | TRACE_TYPE_INFO | TRACE_TYPE_VARS | TRACE_TYPE_FUNC | TRACE_TYPE_RAW; +#define TRACE_LVL6 TRACE_TYPE_ERROR | TRACE_TYPE_PERROR | TRACE_TYPE_INFO | TRACE_TYPE_VARS | TRACE_TYPE_FUNC | TRACE_TYPE_RAW | TRACE_TYPE_DEBUG; + +#define TRACE_LVL_ALL TRACE_LVL6; + +// verbose macroses +#define TRACE( TYPE, LVL, FMT, args...) \ + if( TYPE & LVL ) \ + {\ + if( TYPE & (TRACE_TYPE_INFO | TRACE_TYPE_VARS | TRACE_TYPE_FUNC | TRACE_TYPE_RAW | TRACE_TYPE_DEBUG) ) \ + printf("%s %s: " FMT, __FILE__, __func__ , ## args);\ + \ + else if( TYPE & TRACE_TYPE_DEBUG ) \ + printf("D: %s %s: " FMT, __FILE__, __func__ , ## args);\ + \ + else if( TYPE & TRACE_TYPE_ERROR ) \ + fprintf(stderr, "%s %s: " FMT, __FILE__, __func__ , ## args);\ + \ + else if( TYPE & TRACE_TYPE_PERROR ) \ + {\ + char __data__[256]; \ + memset(__data__,0,256); \ + snprintf(__data__,255,"%s %s [%d]: " FMT, __FILE__, __func__,__LINE__, ## args); \ + perror(__data__); \ + }\ + } + +#define TRACEE( FMT, args... ) TRACE( TRACE_TYPE_ERROR, g_verbose_level, FMT, ## args ) +#define TRACEPE( FMT, args... ) TRACE( TRACE_TYPE_PERROR, g_verbose_level, FMT, ## args ) +#define TRACEI( FMT, args... ) TRACE( TRACE_TYPE_INFO, g_verbose_level, FMT, ## args ) +#define TRACEV( FMT, args... ) TRACE( TRACE_TYPE_VARS, g_verbose_level, FMT, ## args ) +#define TRACEF( FMT, args... ) TRACE( TRACE_TYPE_FUNC, g_verbose_level, FMT, ## args ) +#define TRACER( FMT, args... ) TRACE( TRACE_TYPE_RAW, g_verbose_level, FMT, ## args ) +#define TRACED( FMT, args... ) TRACE( TRACE_TYPE_DEBUG, g_verbose_level, FMT, ## args ) + +#endif //_ADAL_COMMON_H diff --git a/adal_parse.c b/adal_parse.c new file mode 100644 index 0000000..8ecab15 --- /dev/null +++ b/adal_parse.c @@ -0,0 +1,2693 @@ +/* */ +/* OpenPOWER fsp-trace Project */ +/* Contributors Listed Below - COPYRIGHT 2004,2012 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ + +/* need lldiv_t which is in C99 */ +#define _ISOC99_SOURCE +#include <byteswap.h> +#include <ctype.h> +#include <errno.h> +#include <fcntl.h> +#include <math.h> +#include <stdarg.h> +#include <stdlib.h> +#include <sys/stat.h> +#include <unistd.h> +#include <fcntl.h> + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#define __USE_GNU +#endif +#include <search.h> +#include <string.h> + +#include "adal_common.h" +#include "trace_adal.h" + +#define CONVERT_TIME_UNKNOWN 2 + +#define FSP_PIPE_CHUNK_SIZE 64*1024 +#define MAX_FORMAT_STRINGS_COUNT 96*1024 +#define TIMEREF_STRING_FSP1 0x18afc5f5 +#define TIMEREF_STRING 0x04556a8a +#define TRACE_MAX_SIZE 16*1024 +#define TRACE_MAX_BUF_SIZE 512 +#define DEFAULT_TIME_FORMAT "%Y-%m-%d %H:%M:%S" + +#define TRACE_MIN_SIZE (sizeof(trace_entry_stamp_t)+sizeof(trace_entry_head_t)+sizeof(uint32_t)) + +extern int g_verbose_level; +char g_time_format[DEFAULT_FMT_SZ] = ""; + +/* + * @brief default value for time + */ +#define DEFAULT_TIME_FLAG TRACE_TIME_REAL + +// struct for string file "magic cookie" +typedef struct sf_magic_cookie { + uint32_t ver; /* version */ + char date[32]; /* date of creation - timestamp string */ + char build[128]; /* build ID */ +} sf_magic_cookie_t; + +/* struct for trace entry */ +typedef struct trace_entry_i { + trace_entry_stamp_t stamp; + trace_entry_head_t head; + char *bufname; // for pipes + char *data; +} trace_entry_i_t; + + +typedef struct parse_tracebuf_entry { + trace_buf_head_t head; //tracebuffer header + int swap_flg; + size_t te_max; //trace_entry max nr + size_t te_cnt; //trace_entry count + trace_entry_i_t **entries; //ptr to entries +} parse_tracebuf_t; + + +/* struct for string file entry */ +typedef struct sf_entry { +// int32_t hash; + char hash_str[16]; + char *format_str; + char *file; +} sf_entry_t; + +/* struct for trace entry */ +typedef struct { + struct hsearch_data htab; + size_t filled; + sf_entry_t **entries; +} trace_strings_i; + +//---------------------------------------------------------------------------- +// Local function definition +//---------------------------------------------------------------------------- +static int parse_trace_data(char *outbuf, const size_t buf_size, + const trace_entry_i_t * trace_ent, + const trace_strings_i *strings, char ** file, + const int32_t swap_flg); +static int get_format_by_hash(const trace_strings_i *strings, const uint32_t hash, char **format, char **file); +static int get_format_by_hashstr(const trace_strings_i *strings, char *hashstr, char **format, char **file); +static void ppctimebase2timestamp(uint32_t * io_sec, uint32_t * io_usec, uint32_t frequency); +static int locate_timeref(trace_entry_i_t *, trace_timeref_t *, int); +static int convert_time( uint32_t *tbh, uint32_t *tbl, uint32_t time_flg, trace_timeref_t *tmref ); +static int buf_size_valid(uint32_t); + + + +// formated output of trace headers +static void print_trace_struct(trace_entry_i_t * tent) +{ + /* show timestamps, tid; TE_size; TE_type, TE_hash, src_line (trace_adal.h) */ + TRACER ("\n tbh:\t\t0x%08X (%u)\n tbl:\t\t0x%08X (%u)\n tid:\t\t0x%08X (%u)\n" + "\n te_size:\t0x%04X (%u)" + "\n tag:\t\t0x%04X (%u)\n hash nr:\t0x%08X (%u)\n src line:\t0x%08X (%u)" + "\n----------------------------------------\n", + tent->stamp.tbh, tent->stamp.tbh, tent->stamp.tbl, tent->stamp.tbl, tent->stamp.tid, tent->stamp.tid, + tent->head.length, tent->head.length, + tent->head.tag, tent->head.tag, tent->head.hash, tent->head.hash, tent->head.line, tent->head.line); +} + +// 1 - big +// 0 - little +static int is_big_endian(void) +{ + uint32_t i = 0x89abcdef; + unsigned short word; + memcpy(&word, &i, sizeof(unsigned short)); + return word == 0x89ab; +} + + + +static inline uint32_t fsptrace_timebase_frequency(unsigned int flag) +{ + switch (flag) { + case TRACE_TIME_50MHZ: + return 50000000; + case TRACE_TIME_200MHZ: + return 200000000; + case TRACE_TIME_167MHZ: + default: + return 166666667; + } +} + + +/*! + * @brief Parse "magic cookie" from string file. + * Example (v2): + * FSP_TRACE_v2|||Wed Nov 2 13:48:04 2005|||BUILD:/home/bawidama/dev/fsp/apps-ibm-fsp/adals/fsptrace + * + * @param str String for parsing + * @param sf_magic Struct for output values. Contain version, timestamp string, build id. + * Supported versions are numeric from 0 to 2^32; + * + * @return 0 on success, !0 on error. + */ +static int parse_sf_magic_cookie(char *str, sf_magic_cookie_t * sf_magic) +{ + char *pstr, *pstr2; + size_t len; + int ret = -1; + + const char *delim = "|||"; + const char *eyecatch = "#FSP_TRACE_v"; + + TRACEF("E\n"); + if (str == NULL || sf_magic == NULL) + return -1; + + + // looking for version + pstr = strstr(str, eyecatch); + if (!pstr) { + TRACEI("Unable to determine 'magic cookie' (eyecatch)\n"); + goto out; + } + pstr += strlen(eyecatch); + + memset(sf_magic, 0, sizeof(sf_magic_cookie_t)); + sf_magic->ver = atoi(pstr); + + pstr = strstr(pstr, delim); + if (pstr == 0) { + TRACEI("Unable to determine 'magic cookie' (ver)"); + goto out; + } + pstr += strlen(delim); + + // looking for timestamp + pstr2 = strstr(pstr, delim); + len = pstr2 - pstr; + if (pstr2 != 0) { + if (len >= sizeof(sf_magic->date)) { + TRACEI("Unable to determine 'magic cookie' (timestamp).\n"); + goto out; + } + memcpy(sf_magic->date, pstr, len); + sf_magic->date[len] = 0; + } else { + // version founded + sf_magic->date[0] = 0; + sf_magic->build[0] = 0; + ret = 0; + goto out; + } + + + // looking for build id + pstr = pstr2 + strlen(delim); + len = strlen(pstr); + if (pstr2 != 0) { + if (len >= sizeof(sf_magic->build)) { + TRACEI("Unable to determine 'magic cookie' (build).\n"); + goto out; + } + memcpy(sf_magic->build, pstr, len); + sf_magic->build[len] = 0; + if (sf_magic->build[len-1] == '\n') + sf_magic->build[len-1] = 0; + } else { + // version and timestamp found + sf_magic->build[0] = 0; + } + + ret = 0; + +out: + TRACEF("L\n"); + return ret; +} + + + +/*! + * @brief Parse entry string from string_file. + * Example (v2): + * 688650299||trace with 4 params %d %d %d %d||test/trace_test_c.c + * + * @param str String for parsing. + * @param sf_entry Struct for output values. Contain id, format string, string length, source filename. + * If format string is 0 (not allocated), then outputs just string length value. + * + * @return 0 on error, !0 on success. + */ +static int parse_sf_entry_v2(char *str, sf_entry_t * sf_entry) +{ + char *pstr, *pstr2; + size_t len; + int ret = 0; + + const char *delim = "||"; + + memset(sf_entry, 0 , sizeof(sf_entry_t)); + + if (str == NULL || sf_entry == NULL) { + TRACEI("Wrong params.\n"); + goto out; + } + // Looking for hash + pstr = str; + pstr2 = strstr(pstr, delim); + len = pstr2 - pstr; + if (pstr2 == 0 || len >= sizeof(sf_entry->hash_str)) { + TRACEI("Unable to determine id.\n"); + goto out; + } + memcpy(sf_entry->hash_str, pstr, len); + sf_entry->hash_str[len] = 0; + + // Looking for format string + pstr = pstr2 + strlen(delim); + pstr2 = strstr(pstr, delim); + len = pstr2 - pstr; + if (pstr2 == 0 || len <= 0) { + TRACEI("Unable to determine format string.\n"); + goto out; + } + + sf_entry->format_str = (char *) malloc(len + 1); + if (!sf_entry->format_str) { + TRACEPE("malloc error"); + goto out; + } + //printf(" format_str (%u): %x\n",len + 1,sf_entry->format_str); + + memcpy(sf_entry->format_str, pstr, len); + sf_entry->format_str[len] = 0; + + // Looking for filename + pstr = pstr2 + strlen(delim); + len = strlen(pstr); + if (len <= 1) { + TRACEI("Unable to determine filename.\n"); + sf_entry->file = 0; + } + + if( pstr[len-1] == '\n' ) + pstr[len-1] = 0; + + sf_entry->file = (char *) malloc(len + 1); + if (!sf_entry->file) { + TRACEPE("malloc error"); + goto out; + } + + memcpy(sf_entry->file, pstr, len); + sf_entry->file[len] = 0; + + TRACED("Hash:%s format:%s(%"PRIuPTR") file:%s(%"PRIuPTR")\n", sf_entry->hash_str, + sf_entry->format_str, (uintptr_t) sf_entry->format_str, sf_entry->file, + (uintptr_t) sf_entry->file); + + ret = 1; + +out: + if (ret == 0 && sf_entry != NULL) { + if (sf_entry->format_str) { + free(sf_entry->format_str); + sf_entry->format_str = 0; + } + if (sf_entry->file) { + free(sf_entry->file); + sf_entry->file = 0; + } + } + return ret; +} + + +/*! + * @brief Reads a trace string file and adds the information to "strings". + + * + * @param strings Holds information about trace format strings. + * @param infile File where the stringfile will be read from. + * @param flags Can be: + * TRACE_IGNORE_VERSION If set the stringfile is not checked for a magic header line. + * If not set and the check fails the file is not read and an error is returned + * TRACE_OVERWRITE If a string read from infile has the same id as a string that is already in + * strings but the string itself is different, the one from infile replaces + * (overwrites) the conflicting one in strings. Without this flag such a conflict + * is treated as error and an error returned. + * TRACE_VERBOSE When this is set some messages are printed to STDERR. The messages eg. tell + * about checking the file header (TRACE_CHECK_VERSION), about string + * conflicts and a summary about how much strings have been read. There is no + * formal definition of these messages. + * + * @return on success a pointer to a trace_strings_t structure will be returned. + * On error 0 will be returned and errno set accordingly. + */ +trace_strings_t trace_adal_read_stringfile(trace_strings_t i_strings, + const char *infile, int flags) +{ + FILE *fp = 0; + char str[4096]; + char *ptmp, *pstr; + int first, do_free = 0; + sf_magic_cookie_t sf_magic; + sf_entry_t *pent = 0; + int verbose = flags & TRACE_VERBOSE; + int l_errno = 0; + trace_strings_i * strings = (trace_strings_i *) i_strings; + + ENTRY hentry, *hep; + struct hsearch_data *htab; + + + TRACEF("E\n"); + if (infile == NULL || *infile == 0) { + TRACEE("Invalid parameters\n"); + errno = EINVAL; + return 0; + } + if (strings == NULL) { + strings = (trace_strings_i *) malloc(sizeof(*strings)); + if (strings == 0) { + TRACEE("out of memory for strings object\n"); + errno = ENOMEM; + return 0; + } + do_free = 1; + memset(strings, 0, sizeof(trace_strings_i)); + } + htab = &(strings->htab); + if (!strings->entries) { + /* initialize struct */ + TRACEI("Creating hashtable.\n"); + memset(htab, 0, sizeof(struct hsearch_data)); + if (hcreate_r(MAX_FORMAT_STRINGS_COUNT, htab) == 0) { + TRACEPE("hcreate_r failed"); + l_errno = ENOMEM; + goto error; + } + strings->entries = (sf_entry_t **) malloc(MAX_FORMAT_STRINGS_COUNT * sizeof(sf_entry_t *)); + if (!strings->entries) { + TRACEE("out of memory for string pointers\n"); + l_errno = ENOMEM; + goto error; + } + } + + fp = fopen(infile, "r"); + if (!fp) { + TRACEPE("cannot open stringfile '%s'", infile); + l_errno = errno; + goto error; + } + // Read "magic cookie" + pstr = fgets(str, sizeof(str), fp); + if (!pstr) { + TRACEE("cannot read from stringfile '%s'\n", infile); + l_errno = EINVAL; + goto error; + } + // Parse "magic cookie" + memset(&sf_magic, 0, sizeof(sf_magic)); + sf_magic.ver = 2; + if (parse_sf_magic_cookie(str, &sf_magic)) { + if (!(flags & TRACE_IGNORE_VERSION)) { + TRACEE("stringfile magic cookie not found or corrupted.\n"); + l_errno = EINVAL; + goto error; + } else { + if (verbose) { + TRACEI("stringfile magic cookie not found or corrupted.\n"); + } + } + } else { + if (verbose) { + TRACEI("Got stringfile magic cookie. Version: %u Date: %s Build: %s\n", + sf_magic.ver, sf_magic.date, sf_magic.build); + } + if (flags & TRACE_IGNORE_VERSION) { + if (verbose) { + TRACED("Ignore magic cookie (TRACE_IGNORE_VERSION is given).\n"); + } + sf_magic.ver = TRACE_IGNORE_VERSION; + } else if (sf_magic.ver != 2) { + TRACEE("stringfile '%s' has Unknown version %d\n", + infile, sf_magic.ver); + l_errno = EINVAL; + goto error; + } + } + + first = 1; + // Continously read string by string until the end + while (fgets(str, sizeof(str), fp) != NULL) { + // Check entries count + if (strings->filled >= MAX_FORMAT_STRINGS_COUNT) { + if (verbose) + TRACEE("Too many strings (%zu), using only %u\n", + strings->filled, MAX_FORMAT_STRINGS_COUNT); + break; + } + + // allocate mem for entry structure + pent = strings->entries[strings->filled] = + (sf_entry_t *) malloc(sizeof(sf_entry_t)); + if (pent == 0) { + TRACEE("out of memory for strings pointer list"); + l_errno = errno; + goto error; + } + strings->filled++; + + if (parse_sf_entry_v2(str, pent) == 0) { + free(pent); + strings->filled--; + if (first) + TRACEI("Can't parse stringfile entry: '%s'\n", str); + first = 0; + continue; + } + // generate hash-array + hentry.key = pent->hash_str; + hentry.data = (void *) pent; + + if (get_format_by_hashstr(strings, pent->hash_str, &ptmp, 0) >= 0) { + if (!strcmp(pent->format_str, ptmp)) { + /* same string, same hash */ + TRACED("string occurs twice. id:%s string:%s\n", + pent->hash_str, ptmp); + continue; + } + if (flags & TRACE_OVERWRITE) { + TRACEI("Two strings have the same id. Overwrite old format string by new. id:%s old:%s new:%s.\n", + pent->hash_str, pent->format_str, ptmp); + first = 0; + } else { + TRACEI("Two strings have the same id. Skip the stringfile. id:%s old:%s new:%s\n", + pent->hash_str, pent->format_str, ptmp); + l_errno = EAGAIN; + goto error; + } + } + if (hsearch_r(hentry, ENTER, &hep, htab) == 0) { + TRACEPE("Can't add an entry to the hash table (hash:%"PRIuPTR")", + (uintptr_t) hentry.key); + l_errno = ENOMEM; + goto error; + } + TRACED("entered hash %s text '%s'\n", hentry.key, pent->format_str); + } + + if (verbose) + TRACEI("Got %zu strings from stringfile.\n", strings->filled); + + /* success */ + l_errno = 0; + +error: + if (fp) + fclose(fp); + + if (l_errno) { + if (do_free) { + /* error: free strings only if allocated by us */ + trace_adal_free_strings((trace_strings_t) strings); + free(strings); + } + /* return 0 on error */ + strings = NULL; + errno = l_errno; + } + + TRACEF("L\n"); + return (trace_strings_t) strings; +} + + +/*! + * @brief Deallocates the memory allocated with trace_adal_read_stringfile() for "strings". + * + * @param strings Has to point to a trace_string_t structure that holds information about trace format strings. + */ +void trace_adal_free_strings(trace_strings_t i_strings) +{ + sf_entry_t *pent; + size_t i; + trace_strings_i * strings = (trace_strings_i *) i_strings; + + if (strings == NULL) + return; + + TRACEF("E\n"); + + if (strings->entries) { + for (i = 0; i < strings->filled; i++) { + pent = strings->entries[i]; + if (pent != 0) { + free(pent->format_str); + free(pent->file); + free(pent); + } + } + free(strings->entries); + + hdestroy_r(&(strings->htab)); + } + strings->filled = 0; + TRACEF("L\n"); + return; +} + + + +static int32_t set_swap_flag(parse_tracebuf_t * parse_hdr) +{ + unsigned char local_endian = (is_big_endian()? 'B' : 'L'); + + if (parse_hdr->head.endian_flg == local_endian) { + parse_hdr->swap_flg = 0; + } else if (parse_hdr->head.endian_flg == (is_big_endian()? 'L' : 'B')) { + parse_hdr->swap_flg = 1; + } else { + TRACEE("Trace hdr endianess %u\n", parse_hdr->head.endian_flg); + if (buf_size_valid(parse_hdr->head.size)) { + parse_hdr->swap_flg = 0; + TRACEE(" guessing host endianess\n"); + } else if (buf_size_valid(bswap_32(parse_hdr->head.size))) { + parse_hdr->swap_flg = 1; + TRACEE(" guessing non-host endianess\n"); + } else { + /* return -1 to fail, returning 0 will loop forever */ + TRACEE(" invalid size (0x%08x)\n", parse_hdr->head.size); + return -1; + } + } + + return 0; +} + + +/* getting buf_head from beginning of buffer + * return 0 if ok, -1 on error + * return sz if tracebuffer is unused (ver = 0) + * */ +static int fill_parse_header(char * data, size_t sz, parse_tracebuf_t * parse_hdr) +{ + int32_t rc = -1, idx; + trace_buf_head_t * buf_head = &(parse_hdr->head); + + if (sz < sizeof(struct trace_buf_head_v1)) { + TRACEE("sz[%zu] < buf_head_sz\n", sz); + goto exit_gbh; + } + + /* fill parse_hdr with v1. (v2+ data fields unneeded). */ + memcpy(buf_head, data, sizeof(struct trace_buf_head_v1)); + + if (parse_hdr->head.ver != TRACE_BUFFER_VERSION) { + if (!parse_hdr->head.ver) { + rc = set_swap_flag(parse_hdr); + if (rc) return rc; + + /* tracebuffer isn't used, return tracebuffer header size. */ + return (parse_hdr->swap_flg) ? bswap_32(parse_hdr->head.size) : parse_hdr->head.size; + } + TRACEE("Trace hdr version %u\n", parse_hdr->head.ver); + } + + if ((parse_hdr->head.hdr_len != (unsigned char) sizeof(struct trace_buf_head_v1)) && + (parse_hdr->head.hdr_len != (unsigned char) sizeof(struct trace_buf_head_v2))) { + TRACEE("Trace hdr size %u, expected = %zu or %zu\n", parse_hdr->head.hdr_len, + sizeof(struct trace_buf_head_v1), sizeof(struct trace_buf_head_v2)); + goto exit_gbh; + } + + if (convert_time(0, 0, parse_hdr->head.time_flg, 0)) { + TRACEE("Trace hdr timing flag %u\n", parse_hdr->head.time_flg); + goto exit_gbh; + } + + /* reset rc (for failures) */ + rc = set_swap_flag(parse_hdr); + if (rc) goto exit_gbh; + rc = -1; + + for (idx = 0; idx < TRACE_MAX_COMP_NAME_SIZE; idx++) { + if (!parse_hdr->head.comp[idx]) break; + if (!isprint(parse_hdr->head.comp[idx])) { + TRACEE("Trace hdr name is invalid\n"); + goto exit_gbh; + } + } + + if (parse_hdr->swap_flg) { + /* performing endian swap for necessary fields */ + parse_hdr->head.size = bswap_32(parse_hdr->head.size); + parse_hdr->head.times_wrap = bswap_32(parse_hdr->head.times_wrap); + parse_hdr->head.next_free = bswap_32(parse_hdr->head.next_free); + } + + if (sz < parse_hdr->head.size) { + TRACEE("sz(%zu) < buf_head.sz(%u)\n", sz, parse_hdr->head.size); + goto exit_gbh; + } + + if (parse_hdr->head.next_free > parse_hdr->head.size) { + TRACEE("tracebuffer - wrap offset too big (%u, size=%u)\n", + parse_hdr->head.next_free, parse_hdr->head.size); + goto exit_gbh; + } + rc = 0; + +exit_gbh: + return rc; +} + + +/******************************************************************************* + Function saves single trace entry. First, find size of trace. The trace + size is word-aligned (as opposed to being packed). If the trace seems valid, + copy the data to tent, update the ptrace ptr, swap any necessary fields, and + get out of dodge. + + Parameters. + @data: ptr to beginning of trace data, i.e after header. + @ptrace: ptr to last byte of trace. + @tent: Trace Entry parsing pointer. + @swp: binary flag. Do we need to endian swap data. + + Returns pointer to next trace. +*******************************************************************************/ + +static char *get_trace(char *data, char *ptrace, trace_entry_i_t * tent, uint32_t swp) +{ + uint32_t trace_sz = 0; + size_t head_size = sizeof(trace_entry_stamp_t) + sizeof(trace_entry_head_t); + + if (ptrace <= data || !tent) + return 0; + + /* save trace size (ptrace points last byte of chained size) */ + TRACEI("ptrace - sizeof(trace_sz), sizeof(trace_sz) %"PRIuPTR" %zu \n", (uintptr_t)ptrace - sizeof(trace_sz), sizeof(trace_sz)); + memcpy(&trace_sz, ptrace - sizeof(trace_sz), sizeof(trace_sz)); + if (swp) trace_sz = bswap_32(trace_sz); + + if (trace_sz % 4) { //corrupted + TRACEI("TE is unaligned : %u. Skip.\n", trace_sz); + return 0; + } + + if ((unsigned long) ptrace < (unsigned long) trace_sz) { + TRACEE("TE is too big. Skip\n"); + return 0; + } + + if (!trace_sz || (ptrace - trace_sz) < data) { + // TRACEI("Last, wrapped, or corrupted TE. Done"); + return 0; + } + + /* update TE ptr */ + ptrace -= trace_sz; + + /* save, swap, trace_ent ptr. */ + memcpy(tent, ptrace, head_size); + tent->data = ptrace + head_size; + if (swp) { + tent->stamp.tbh = bswap_32(tent->stamp.tbh); + tent->stamp.tbl = bswap_32(tent->stamp.tbl); + tent->stamp.tid = bswap_32(tent->stamp.tid); + + tent->head.length = bswap_16(tent->head.length); + tent->head.tag = bswap_16(tent->head.tag); + tent->head.hash = bswap_32(tent->head.hash); + tent->head.line = bswap_32(tent->head.line); + } + + /* trace_sz includes trace header and chain size */ + if (tent->head.length + head_size > trace_sz - 4) { + /* trace entry truncated by device driver */ + tent->head.length = trace_sz - head_size - 4; + } + + return ptrace; +} + + + +// getting trace from tracBINARY pipe buffer +// ptrace must locate begin of the trace +// return pointer to next trace +// Note: the buffername isn't copied but just linked with a pointer into the trace_ent structure, +// so you can't use it after 'data' memory release! +static char *get_pipe_trace(char *data, size_t size, trace_entry_i_t * trace_ent, uint32_t swap) +{ + if (data == 0 || trace_ent == 0) + return 0; + + size_t head_size = sizeof(trace_entry_stamp_t) + sizeof(trace_entry_head_t); + char *pos = data; + char *data_end = data + size; + char *ret = 0; + + if (size <= head_size + 2) { + /* too small (header + zero-terminated buffer name, + * maybe need to read next chunk */ + TRACED("Trace from pipe too small (%zu)\n", size); + return 0; + } + if (strnlen(data, TRACE_MAX_COMP_NAME_SIZE) >= TRACE_MAX_COMP_NAME_SIZE) { + TRACEE("Trace component name corrupted: %.15s\n", data); + return 0; + } + pos = strchr(data, 0) + 1; + + trace_ent->bufname = data; + memcpy(trace_ent, pos, head_size); + pos += head_size; + + TRACED("data: 0x%"PRIuPTR" data_end: 0x%"PRIuPTR" pos: 0x%"PRIuPTR" trace_ent->head.length: 0x%X swap:%d\n", (uintptr_t)data, (uintptr_t)data_end, (uintptr_t)pos, (int)trace_ent->head.length, (int)swap); + if (swap) { + trace_ent->stamp.tbh = bswap_32(trace_ent->stamp.tbh); + trace_ent->stamp.tbl = bswap_32(trace_ent->stamp.tbl); + trace_ent->stamp.tid = bswap_32(trace_ent->stamp.tid); + + trace_ent->head.length = bswap_16(trace_ent->head.length); + trace_ent->head.tag = bswap_16(trace_ent->head.tag); + trace_ent->head.hash = bswap_32(trace_ent->head.hash); + trace_ent->head.line = bswap_32(trace_ent->head.line); + } + + if ((pos + trace_ent->head.length) > data_end) { + TRACED("Trace is cuted(body), skip. (trace_ent->head.length: %d)\n", trace_ent->head.length); + return 0; + } + + trace_ent->data = pos; + ret = pos + trace_ent->head.length; + return ret; +} + +/* unwrap buffer + * return 0 if ok, -1 on error */ +static int unwrap_buf(char *data, size_t size, uint32_t part1_size) +{ + if (!data) return -1; + + int part2_size = size - part1_size; + char *temp = (char *) malloc(size); + if (!temp) return -1; + + memcpy(temp, data, part1_size); + memmove(data, data + part1_size, part2_size); + memcpy(data + part2_size, temp, part1_size); + + free(temp); + return 0; +} + + +// unallocate entries list +// parse_hdr - first entry +static void free_entries_list(parse_tracebuf_t * parse_hdr) +{ + if (!parse_hdr) + return; + + if (parse_hdr->entries && parse_hdr->te_cnt) { + unsigned int i; + for(i=0; i < parse_hdr->te_cnt; i++) { + free(parse_hdr->entries[i]); + } + free(parse_hdr->entries); + } +} + +/* return 0 if ok, -1 on error */ +static int time2epoch(uint32_t * sec, uint32_t * usec, trace_timeref_t * timeref) +{ + int ret = 0; + + if (!sec || !usec) { + ret = -1; + goto out; + } + + if (!timeref) { + goto out; + } + TRACED("tod=%u.%06u runtime=%u.%06u\n", (unsigned)timeref->tod.tv_sec, + (unsigned)timeref->tod.tv_usec, + timeref->time_stamp.high, timeref->time_stamp.low); + TRACEV("%x.%08x ", *sec, *usec); + + /* timeref contains a pair of tod/stamp values. tod is the unix time + * (number of seconds) when the ppc tick counter had the value that + * was stored in stamp. The stamp value is already converted to seconds + * + microseconds since-boot as is the stamp in sec/usec params. + * Therefore we calculate the tod of the trace as: + * sec/usec + timeref.tod - timeref.stamp + */ + + *sec += (timeref->tod.tv_sec - timeref->time_stamp.high); + *usec += (timeref->tod.tv_usec - timeref->time_stamp.low); + + if ( *usec > 2000000) { + /* underflow, usec is in fact negative */ + *usec = *usec + 1000000; + *sec = *sec - 1; + } else if ( *usec > 1000000) { + /* overflow */ + *usec = *usec - 1000000; + *sec = *sec + 1; + } + TRACEV(" -> %u.%06u\n", *sec, *usec); +out: + return ret; +} + +static int buf_size_valid(uint32_t size) { + if (size & 3) return 0; + if (size < sizeof(struct trace_buf_head_v1)) return 0; + if (size > sizeof(struct trace_buf_head_v2)) return 0; + if (size > 128*1024) return 0; /* need to change if >128k supported */ + return 1; +} + + +/* + * @brief The buffer parsing - getting all traces + * There is no memory allocation for the trace body - used pointer to the given buffer. + * + * @param data -- pointer to list of unparsed tracebuffers in a single file + * @param size -- size of tracebuffer + * @param parse_hdr -- buffer struct to fill with hdr, trace entries + * + * @return number of bytes read or -1 if error. + */ +static int parse_tracebuffer(char *data, size_t sz, parse_tracebuf_t * parse_hdr, trace_timeref_t * time, int * found_time) +{ + int32_t idx, rc = -1; + char *ptrace; + unsigned char local_endian_flg = (is_big_endian()? 'B' : 'L'); + + if (!data || !parse_hdr || !sz) { + return 0; + } + + parse_hdr->entries = 0; + parse_hdr->te_cnt = 0; + + TRACEF("E\n"); + + /* copy tracebuffer hdr into parse_header */ + rc = fill_parse_header(data, sz, parse_hdr); + if (rc) goto exit_ptb; + + TRACER("\n------------------------------------\n" + "buf_head\n" + "------------------------------------\n" + " ver:\t\t0x%02X (%u)\n" + " hdr_len:\t0x%02X (%u)\n" + " time_flg:\t0x%02X (%u)\n" + " endian_flg:\t0x%02X (%c)\n" + "* local_end_flg:0x%02X (%c)\n" + " comp:\t\t%s\n" + " size:\t\t0x%08X (%u)\n" + " times_wrap:\t0x%08X (%u)\n" + " next_free:\t0x%08X (%u)\n" + "------------------------------------\n", + parse_hdr->head.ver, parse_hdr->head.ver, + parse_hdr->head.hdr_len, parse_hdr->head.hdr_len, + parse_hdr->head.time_flg, parse_hdr->head.time_flg, + parse_hdr->head.endian_flg, parse_hdr->head.endian_flg, + local_endian_flg, local_endian_flg, + parse_hdr->head.comp, + parse_hdr->head.size, parse_hdr->head.size, + parse_hdr->head.times_wrap, parse_hdr->head.times_wrap, + parse_hdr->head.next_free, parse_hdr->head.next_free); + + if (parse_hdr->head.times_wrap) { + unwrap_buf(data + parse_hdr->head.hdr_len, + parse_hdr->head.size - parse_hdr->head.hdr_len, + parse_hdr->head.next_free - parse_hdr->head.hdr_len); + parse_hdr->head.next_free = parse_hdr->head.size; + } + //print_dump(data,sz); + + /* initialize parse_hdr. Set list sz to max, malloc mem. */ + parse_hdr->te_max = parse_hdr->head.size / TRACE_MIN_SIZE; + parse_hdr->entries = (trace_entry_i_t **) malloc(sizeof(void *) * parse_hdr->te_max); + if (!parse_hdr->entries) { + TRACEE("OOM - TE ptrs\n"); + rc = -ENOMEM; + goto exit_ptb; + } + + // looking for the end of the last trace + ptrace = data + parse_hdr->head.next_free; + + /* fill TE array from end. */ + idx = (int) parse_hdr->te_max - 1; + while (1) { + trace_entry_i_t * tent = (trace_entry_i_t *) malloc(sizeof(trace_entry_i_t)); + if (!tent) { + TRACEE("OOM - TE struct\n"); + rc = -ENOMEM; + goto error; + } + + ptrace = get_trace(data + parse_hdr->head.hdr_len, ptrace, tent, parse_hdr->swap_flg); + if (!ptrace) { + /* done */ + free(tent); + break; + } + + tent->bufname = parse_hdr->head.comp; + parse_hdr->entries[idx] = tent; + idx--; + if (idx < 0) { + /* shouldn't happen! - hand over what we have so far */ + TRACEE("too many trace entries in buffer, aborting\n"); + break; + } + print_trace_struct(tent); + + if (*found_time == 0) { + *found_time = locate_timeref(tent, time, parse_hdr->swap_flg); + } + } + + if (idx < (int) parse_hdr->te_max - 1) { + /* found at least one trace_entry, */ + int found = parse_hdr->te_max-1 - idx; + if (idx >= 0) { + /* move found trace_entries to begining of list. */ + memmove(parse_hdr->entries, parse_hdr->entries+idx+1, found * sizeof(void*)); + } + parse_hdr->te_cnt = found; + } else { + free(parse_hdr->entries); + parse_hdr->entries = 0; + parse_hdr->te_max = 0; + } + TRACEF("L\n"); + return parse_hdr->head.size; + +error: + free_entries_list(parse_hdr); +exit_ptb: + TRACEF("L\n"); + return rc; +} + +/* return 1 on ok, 0 on error */ +static int locate_timeref(trace_entry_i_t *parse_hdr, trace_timeref_t *timeref, int swp) +{ + int ret = 0; + + if (!parse_hdr || !timeref) { + goto out; + } + + //printf("tag:0x%X hash:0x%X\n", parse_hdr->head.tag, parse_hdr->head.hash); + // try to locate the TIMEREF trace + if(parse_hdr->head.tag == TRACE_COMP_TRACE && (parse_hdr->head.hash == TIMEREF_STRING || parse_hdr->head.hash == TIMEREF_STRING_FSP1)) { + uint32_t *pint = (uint32_t *) parse_hdr->data; + + if (swp) { + timeref->time_stamp.high = bswap_32(pint[2]); + timeref->time_stamp.low = bswap_32(pint[3]); + timeref->tod.tv_sec = bswap_32(pint[0]); + timeref->tod.tv_usec = bswap_32(pint[1]); + if (parse_hdr->head.hash != TIMEREF_STRING) { + timeref->frequency = bswap_32(pint[4]); + } else { + /* should be from FSP0, assume 200MHz */ + timeref->frequency = 200000000; + } + } else { + timeref->tod.tv_sec = pint[0]; + timeref->tod.tv_usec = pint[1]; + timeref->time_stamp.high = pint[2]; + timeref->time_stamp.low = pint[3]; + if (parse_hdr->head.hash != TIMEREF_STRING) { + timeref->frequency = pint[4]; + } else { + /* should be from FSP0, assume 200MHz */ + timeref->frequency = 200000000; + } + } + + /* fix bad freq value */ + if (timeref->frequency == 167666667) + timeref->frequency = 166666667; + + if (timeref->frequency) + ppctimebase2timestamp((uint32_t*)&(timeref->time_stamp.high), (uint32_t*)&(timeref->time_stamp.low), timeref->frequency); + //printf("time1: %x.%x *** %x.%x *** freq:%X\n", (uint32_t)timeref->time_stamp.high, (uint32_t)timeref->time_stamp.low, timeref->tod.tv_sec, timeref->tod.tv_usec, timeref->frequency); + + ret = 1; + } +out: + TRACED("found? %d\n", ret); + return ret; +} + + +// convert PPC timebase 64 bit value to timestamp - sec,usec +// tbh and tbl can be 0, then function just validate the time_flg value +// return: +// 0 - ok, the time has been converted +// CONVERT_TIME_UNKNOWN - unknown value of time_flg, the time has not been converted +static int convert_time(uint32_t *tbh, uint32_t *tbl, uint32_t time_flg, trace_timeref_t *tmref ) +{ + int ret = 0; + int epoch_flg = 0; + + // save and reset epoch flag + epoch_flg = time_flg & TRACE_TIME_EPOCH; + if( time_flg != TRACE_TIME_UNKNOWN ) + time_flg &= ~TRACE_TIME_EPOCH; + + // convert + switch( time_flg ) { + case TRACE_TIME_REAL: + case TRACE_TIME_TIMESPEC: + break; + + case TRACE_TIME_50MHZ: + case TRACE_TIME_200MHZ: + case TRACE_TIME_167MHZ: + /* convert the reference 64 bit ppc time stamp to sec/usec */ + if (tbh && tbl) + ppctimebase2timestamp(tbh, tbl, fsptrace_timebase_frequency(time_flg)); + + /* make the time epoch-based if it isn't already */ + if (!epoch_flg) + time2epoch(tbh, tbl, tmref); + break; + + case TRACE_TIME_UNKNOWN: + + if (tmref && tbh && tbl) { + /* convert the reference 64 bit ppc time stamp to sec/usec */ + ppctimebase2timestamp(tbh, tbl, fsptrace_timebase_frequency(tmref->frequency)); + /* make the time epoch-based */ + time2epoch(tbh, tbl, tmref); + } + + break; + + default: + ret = CONVERT_TIME_UNKNOWN; + break; + } + + // restore epoch flag + if( time_flg != TRACE_TIME_UNKNOWN ) + time_flg |= epoch_flg; + + return ret; +} + + +// compare traces by time +// return: <0 - pent1 < pent2 +// 0 - pent1 = pent2 +// >0 - pent1 > pent2 +static int trace_list_compare(trace_entry_i_t * pent1, trace_entry_i_t * pent2) +{ + if (pent1 == 0 || pent2 == 0) + return 0; + + register uint32_t tbh1 = pent1->stamp.tbh; + register uint32_t tbl1 = pent1->stamp.tbl; + register uint32_t tbh2 = pent2->stamp.tbh; + register uint32_t tbl2 = pent2->stamp.tbl; + + if (tbh1 > tbh2) + return 1; + if (tbh1 < tbh2) + return -1; + if (tbl1 > tbl2) + return 1; + if (tbl1 < tbl2) + return -1; + return 0; +} + +/* merge sort: merge two sorted arrays into one big sorted array */ +static int trace_array_merge_sort(parse_tracebuf_t * buf1, + parse_tracebuf_t * buf2, + trace_entry_i_t *** array_out, size_t *sizeout) +{ + trace_entry_i_t **pout; + size_t i1, i2, size1, size2; + + size1 = buf1->te_cnt; + size2 = buf2->te_cnt; + + i1 = 0; + i2 = 0; + pout = (trace_entry_i_t **) malloc(sizeof(void*) * (size1 + size2)); + if (pout == NULL) { + TRACEE("out of memory merge-sorting trace lists\n"); + return -ENOMEM; + } + *array_out = pout; + while(i1 < size1 && i2 < size2) { + if (trace_list_compare(buf1->entries[i1], buf2->entries[i2]) < 0) { + *pout = buf1->entries[i1]; + pout++; + i1++; + } else { + *pout = buf2->entries[i2]; + pout++; + i2++; + } + } + while(i1 < size1) { + *pout++ = buf1->entries[i1++]; + } + while(i2 < size2) { + *pout++ = buf2->entries[i2++]; + } + *sizeout = size1 + size2; + + return 0; +} + + +static int trace_output_get_format(int flags, char *head_fmt, size_t head_size, + char *entry_fmt, size_t entry_size, + char *foot_fmt, size_t foot_size) +{ + // Output format: " timestamp | tid | [comp] | line | message | [comp] | [filename]" + // " %08u.%06u | %5u | [%-16s] | %4u | %s | [%-16s]| [%s] " + + char *head_caption = " %s\n"; + char *head_timestamp = " Sec Usec "; + char *head_timeofday = " Sec Usec "; + char *head_comp_pre = " Comp "; + char *head_comp_app = " Comp "; + char *head_tid = " PID"; + char *head_message = " Entry Data"; + char *head_filename = " Filename "; + char *head_line = " Line"; + + char *entry_timestamp = "%1$08u.%2$09u"; + char *entry_timeofday = "%1$s.%2$06u"; + char *entry_comp_pre = "|%3$-16s"; + char *entry_comp_app = "|%3$-16s"; + char *entry_tid = "|%4$5u"; + char *entry_message = "|%5$s"; + char *entry_filename = "|%6$s"; + char *entry_line = "|%7$4u"; + + // output templates + char *delim = + "-------------------------------------------------------------------------------\n"; + char *foot = " %u traces read.\n"; + char *empty = ""; + + // comp prepend + if (!(flags & TRACE_PREPEND_BUFFERNAME)) { + entry_comp_pre = empty; + head_comp_pre = empty; + } + // comp append + if (!(flags & TRACE_APPEND_BUFFERNAME) + || (flags & TRACE_PREPEND_BUFFERNAME)) { + entry_comp_app = empty; + head_comp_app = empty; + } + // filename + if (!(flags & TRACE_FILENAME)) { + entry_filename = empty; + head_filename = empty; + } + // timeofday + if (flags & TRACE_TIMEOFDAY) { + entry_timestamp = empty; + head_timestamp = empty; + } else { + entry_timeofday = empty; + head_timeofday = empty; + } + + if (flags & TRACE_TIME_STRFTIME) { + entry_timeofday = "%1$s"; + } + + // Output format: " | timestamp \| [comp] \| tid \| message \| [comp] \| [filename]" + // "%c| %08u.%09u \| [%s] \| %5u \| %s \| [%s] \| [%s] " + if (entry_fmt) { + if (snprintf(entry_fmt, entry_size, "%s%s%s%s%s%s%s%s\n", + entry_timestamp, entry_timeofday, entry_tid, entry_comp_pre, + entry_line, entry_message, entry_comp_app, entry_filename) < 0 ) { + *entry_fmt = 0; + return -1; + } + } + + if (head_fmt) { + if( snprintf(head_fmt, head_size, "%sTRACEBUFFER:%s%s%s%s%s%s%s%s%s%s\n%s", + delim, head_caption, delim, head_timestamp, + head_timeofday, head_tid, head_comp_pre, head_line, + head_message, head_comp_app, head_filename, delim) < 0 ) { + *head_fmt = 0; + return -1; + } + } + + if (foot_fmt) { + if( snprintf(foot_fmt, foot_size, "%s%s", delim, foot) < 0 ) { + *foot_fmt = 0; + return -1; + } + } + return 0; +} + +static int trace_output(FILE* fp, char *fmt, ...) +{ + va_list arg_ptr; + int ret; + + if (fmt == 0) + return -EINVAL; + + va_start(arg_ptr, fmt); + ret = vfprintf(fp, fmt, arg_ptr); + va_end(arg_ptr); + return ret; +} + + +// Formating and output of traces +// flags - flags from print_buffers +static int trace_output_entry(FILE *fp, char *entry_fmt, + trace_entry_i_t * trace_ent, + trace_strings_i *strings, int swap_flg, + int flags, char *time_format) +{ + char buf[TRACE_MAX_SIZE]; + char *nextstr; + char *curstr = buf; + char *file = 0; + char timestr[DEFAULT_FMT_SZ]; + char *storage_format; + int ret, written = 0; + int time_flg = 0; + time_t gtime = 0; + + // parse message + if (parse_trace_data(buf, sizeof(buf), trace_ent, strings, &file, swap_flg) == -1) { + TRACED("parse_trace_data failed.\n"); + return -EINVAL; + } + + curstr = buf; + if (flags & TRACE_TIME_STRFTIME) { + + storage_format = malloc(DEFAULT_FMT_SZ); + memset(storage_format, 0, DEFAULT_FMT_SZ); + memcpy(storage_format, time_format, DEFAULT_FMT_SZ); + + char *p = storage_format; + while ((p = strchr(p, '%')) != NULL) { + int n, m; + unsigned pres, scale; + + p++; + if (*p == '%') { + p++; + continue; + } + n = strspn(p, "0123456789"); + if (p[n] != 'N') { + p += n; + continue; + } + /* We have "%[nnn]N" */ + p[-1] = '\0'; + p[n] = '\0'; + scale = 1; + pres = 9; + if (n) { + pres = atoi(p); + if (pres == 0) + pres = 9; + m = 9 - pres; + while (--m >= 0) + scale *= 10; + } + + m = p - storage_format; + p += n + 1; + asprintf(&storage_format, "%s%0*u%s", storage_format, + pres, (unsigned)trace_ent->stamp.tbl / scale, p); + p = storage_format + m; + } + gtime = trace_ent->stamp.tbh; + strftime(timestr, sizeof(timestr), storage_format, + gmtime(>ime)); + time_flg = 1; + free(storage_format); + + } + + else if (flags & TRACE_TIMEOFDAY) { + gtime = trace_ent->stamp.tbh; + strftime(timestr, sizeof(timestr), DEFAULT_TIME_FORMAT, + gmtime(>ime)); + time_flg = 1; + } + + do { + nextstr = strchr(curstr, '\n'); + if (nextstr != 0) + *nextstr = '\0'; + + if (time_flg) + ret = trace_output(fp, entry_fmt, + timestr, 0, trace_ent->bufname, + TRACE_TID_TID(trace_ent->stamp.tid), curstr, + file, trace_ent->head.line); + else + ret = trace_output(fp, entry_fmt, + (char *)((uintptr_t)trace_ent->stamp.tbh), + trace_ent->stamp.tbl, trace_ent->bufname, + TRACE_TID_TID(trace_ent->stamp.tid), curstr, + file, trace_ent->head.line); + + if (ret < 0) + return ret; + written += ret; + if (nextstr == NULL) + break; + curstr = nextstr + 1; + } + while ((uintptr_t) curstr != 1); // curstr = strchr( curstr, '\n') + 1; + + return written; +} + + +static void ppctimebase2timestamp(uint32_t * io_sec, uint32_t * io_usec, uint32_t frequency) +{ + unsigned long long longlong; + lldiv_t div; + + TRACEV("%08x.%08x ", *io_sec, *io_usec); + /* make one 64 bit timestamp */ + longlong = *io_sec; + longlong <<= 32; /* cannot use *io_sec<<32 as this would be 0 */ + longlong += *io_usec; + div = lldiv(longlong, frequency); + /* quot is number of seconds */ + *io_sec = (uint32_t) div.quot; + /* rem is number of ticks left in unfinished second */ + /* translate to usecs, the divide by frequency */ + longlong = div.rem * 1000000; + div = lldiv(longlong, frequency); + *io_usec = (uint32_t) div.quot; + TRACEV(" => %u.%06u (%u)\n", *io_sec, *io_usec, frequency); +} + + +static int trace_output_vbuf(int outfile, parse_tracebuf_t ** parse_hdr, + int buf_count, trace_strings_i *strings, + trace_timeref_t * timeref, int flags, int mixed, + char *time_format) +{ + //TODO: timeofday support, filename output support + + if (parse_hdr == 0 || buf_count < 0) + return -EINVAL; + + int i=0, ret=0, traces_count = 0; + int outfile_dup; + + char head_fmt[1024]; + char entry_fmt[1024]; + char foot_fmt[1024]; + size_t zheader_size = 256; + char temp[zheader_size]; + FILE* fp; + + int binary = flags & TRACE_BINARY; + + TRACEI("(%d, %p, %d, %p, %p, %d, %d\n", outfile, *parse_hdr, buf_count, + strings, timeref, flags, mixed); + + /* fclose shouldn't close outfile, therefore we need to dup */ + if (outfile != -1) { + outfile_dup = dup(outfile); + if (outfile_dup < 0) { + TRACEPE("dup of %d failed", outfile); + return -errno; + } + fp = fdopen(outfile_dup, "a"); + if( fp == NULL ) { + TRACEPE("fdopen of %d failed", outfile_dup); + ret = -errno; + close(outfile_dup); + return ret; + } + } + + TRACEI("Output %d buffers in %s format to fd:%d\n", buf_count, binary?"binary":"ascii", outfile); + + if (flags & TRACE_TIMEOFDAY) + { + TRACEI("Time converting (time_flg:%d)\n", parse_hdr[i]->head.time_flg); + ret = convert_time( 0, 0, parse_hdr[i]->head.time_flg, 0 ); + if( ret == CONVERT_TIME_UNKNOWN ) + { + TRACEE("Time format unknown(%u), -k option is disabled.\n", parse_hdr[i]->head.time_flg); + flags &= ~TRACE_TIMEOFDAY; + } else if( timeref == 0 ) { + TRACEE("Timeref has not given and has not founded in buffer, -k option is disabled.\n"); + flags &= ~TRACE_TIMEOFDAY; + } + } + + + // get output formats for header, entry and footer + if (!binary) { + if (trace_output_get_format(flags, head_fmt, sizeof(head_fmt), + entry_fmt, sizeof(entry_fmt), + foot_fmt, sizeof(foot_fmt)) == -1) { + TRACEE("Cannot get output format\n"); + ret = -EAGAIN; + goto out; + } + } + + // process every buffer + for (i = 0; i < buf_count; i++) { + unsigned int tidx; + trace_entry_i_t *trace_ent; + // output header + if (!binary && outfile != -1) { + snprintf(temp, zheader_size, "%s wrapped:%d size:%d", + parse_hdr[i]->head.comp, + parse_hdr[i]->head.times_wrap, + parse_hdr[i]->head.size); + ret = trace_output(fp, head_fmt, mixed ? "Mixed buffer" + : temp); + if (ret < 0) + goto out; + TRACEI("%d buffer header output done\n", i); + } + + // get the first entry + if (parse_hdr[i]->entries == 0) { + if (outfile != -1) { + ret = fprintf(fp, "Buffer is empty.\n"); + if (ret < 0) + goto out; + } + continue; + } + + // output traces one by one + for(tidx = 0; tidx < parse_hdr[i]->te_cnt; tidx++) { + trace_ent = parse_hdr[i]->entries[tidx]; + //print_trace_struct(trace_ent); //debug only + + ret = convert_time( &(trace_ent->stamp.tbh), + &(trace_ent->stamp.tbl), + parse_hdr[i]->head.time_flg, + timeref); + if (outfile != -1) { + ret = trace_output_entry(fp, entry_fmt, + trace_ent, strings, + parse_hdr[i]->swap_flg, + flags, + time_format); + if (ret < 0) + goto out; + } + traces_count++; + } + TRACEI("Buffer %d traces output done\n", i); + } + + // output footer (one for all buffers) +out: + if (outfile != -1) { + if (ret == 0) + ret = trace_output(fp, foot_fmt, traces_count); + fclose(fp); + } + TRACEI("Footer output done\n"); + if (ret < 0) + return ret; + return traces_count; +} + + +/*! + * @brief Parses a trace buffer, splits it into the trace entries and writes the traces formatted to the file "outfile". + * + * @param vec Points to a list (nr = vecsize) of struct iovec elements. Each with pointer to trace buffer array. + * @param outfile File descriptor where the traces should be written to using the "write" system call. + * If outfile is "-1" no output is generated. This can be used to check if a buffer is valid + * and to look for timref values from a TIMEREF trace. + * @param strings Has to point to a trace_string_t structure that holds information about trace format strings. + * This structure has to be created with trace_adal_read_stringfile(). + * @param timeref Has to contain a pointer to a trace_timeref_t structure (cf. Section 1.5.4.2, "Trace with time reference + * and timebase information") if one of the flags TRACE_TIMEOFDAY and TRACE_SET_TIMEOFDAY is set. + * This structure contains a pair of time values and the timestamp frequency. These are used to translate the + * timestamp of the traces into timeofday values. If the timeref is 0 timestamp translation is only possible if a + * trace buffer contains a TIMEREF trace entry. Traces read and formatted prior to reading this trace entry are + * shown with untranslated timestamps. + * @param flags Defines the output. It is the "OR"'ed value of some of the following flags: + * - TRACE_MIX_BUFFERS + * When multiple buffers are given the traces of all buffers are sorted by timestamp and printed as one list. + * If this flag is not given the traces are printed separatly for each trace buffers (i.e. grouped by buffer). + * - TRACE_PREPEND_BUFFERNAME + * Show the name of a trace buffer for each trace. The buffer name will be inserted between timestamp and trace text. + * Only one of TRACE_APPEND_BUFFERNAME and TRACE_PREPEND_BUFFERNAME can be given. + * - TRACE_APPEND_BUFFERNAME + * Show the name of a trace buffer for each trace. The buffer name will be appended at the end of the line + * (after trace text). Only one of TRACE_APPEND_BUFFERNAME and TRACE_PREPEND_BUFFERNAME can be given. + * - TRACE_TIMEOFDAY + * When set timestamps are translated to timeofday values (date/time). This needs "timeref" to be given. + * If timeref is not given the timestamps are treated as if the PPC timebase counter was started at epoch time + * (i.e. the printed timestamp will be the time since FSP boot time). + * - TRACE_SET_TIMEOFDAY If a TIMEREF trace is found in a trace buffer and timeref is a valid + * pointer the values from the TIMEREF trace are written to timeref. This flag is independent of TRACE_TIMEOFDAY. + * - TRACE_FILENAME + * Show the name of the source file that contains the trace statement for each trace. + * (at the end of the line, after buffer name if this is printed too). + * - TRACE_VERBOSE When this is set some messages are printed to STDERR. The messages + * eg. tell about the processed trace buffers (version, size ...), number of + * traces in them etc. There is no formal definition of these messages. + * + * @return on success the number of traces written is returned. On failure a value <0 is returned. + */ +int trace_adal_print_buffers(const struct iovec *vec, size_t vecsize, + int outfile, const trace_strings_t strings, + trace_timeref_t * timeref, int flags) +{ + if (!vec || !vecsize) + return -1; + + parse_tracebuf_t **parse_hdr; /* struct for parsing tracebuffer, holds [buffers]->entries */ + int traces_count = 0; /* from print func, returned if succ */ + size_t buffers_count = 0; /* the number of buffers in parse_hdr */ + size_t output_count = 0; /* number of used buffers in parse_hdr */ + size_t list_size = vecsize; /* length of parse_hdr */ + size_t i; /* loop var for parse_hdr[i] */ + int ret = 0; /* function return value */ + trace_timeref_t tmref; /* to hold time translation info */ + int have_timeref = 0; /* do we have info in tmref? */ + + TRACEF("E\n"); + TRACED("Buffer count: %zu\n", vecsize); + + if (timeref) { + memcpy(&tmref, timeref, sizeof(trace_timeref_t)); + have_timeref = 1; + } else { + memset(&tmref, 0, sizeof(tmref)); + } + + /* allocate memory for parse_tracebuf list */ + parse_hdr = (parse_tracebuf_t **) malloc(list_size * sizeof(void*)); + if (!parse_hdr) { + TRACEE("OOM - parsed buffer list"); + ret = -ENOMEM; + goto out; + } + + /* walk files parsing tracebuffers */ + for (i = 0; i < vecsize; i++, vec++) { + unsigned int bytes_read = 0; + int parts = 0; + + if (!vec) { + TRACED("vec now empty."); + ret = -ENOENT; + goto out; + } + + /* read vec->iov_len of tracefile */ + while (bytes_read < vec->iov_len) { + int32_t rc; + + if (buffers_count > list_size) + abort(); + + /* increase parse_hdr list if needed */ + if (buffers_count == list_size) { + void * p = realloc(parse_hdr, (list_size + 16)*sizeof(void *)); + if (!p) { + TRACEE("OOM - grow parsed buffer list"); + ret = -ENOMEM; + goto out_free; + } + parse_hdr = (parse_tracebuf_t **) p; + memset(parse_hdr+buffers_count, 0, 16*sizeof(void *)); + list_size += 16; + } + + /* allocate memory for next buffer header */ + parse_hdr[buffers_count] = (parse_tracebuf_t *) malloc(sizeof(parse_tracebuf_t)); + if (!parse_hdr[buffers_count]) { + TRACEE("OOM - preparing to parse buffer"); + ret = -ENOMEM; + goto out_free; + } + + /* clear parse_hdr, fill with trace entries. rc = bytes read */ + memset(parse_hdr[buffers_count], 0, sizeof(parse_tracebuf_t)); + rc = parse_tracebuffer((char *)vec->iov_base + bytes_read, + vec->iov_len - bytes_read, + parse_hdr[buffers_count], + &tmref, &have_timeref); + if (rc < 0) { + TRACEE("Parsing failed for buffer %zu after %d parts.\n", i, parts); + free(parse_hdr[buffers_count]); + break; + } + + if (have_timeref) { + if ((flags & TRACE_SET_TIMEOFDAY) && timeref ) { + /* copy found timeref back to caller */ + memcpy(timeref, &tmref, sizeof(trace_timeref_t)); + } + } + + TRACEI("Parsed comp '%s' (%d/%zu bytes)\n", + parse_hdr[buffers_count]->head.comp, rc, + vec->iov_len); + + parts++; + buffers_count++; + bytes_read += rc; + } + TRACEI("buffer %zu had %d parts (%zu bytes left)\n", + i, parts, vec->iov_len - bytes_read); + } + output_count = buffers_count; + TRACEI("%zu buffers parsed.\n", output_count); + + if (buffers_count > 1 && (flags & TRACE_MIX_BUFFERS)) { + // Merge the buffers. + // Create one common list of traces. + // Hint: As we need to sort common list in reverse( from max to min) order so let's link + // buffers smart - a buffer with a minimal time of the first trace place at the start of + // the list and so for. It should speed up further sorting, especially if the buffers + // are not overlaped by time. + + //TODO: generate common caption + + trace_entry_i_t **mergedlist; + size_t mergedsize; + + TRACEI("Mix %zu buffers to one.\n", output_count); + + for (i = 1; i < buffers_count; i++) { + /* merge i. buffer with previous merged (in [0]) */ + ret = trace_array_merge_sort( + parse_hdr[0], parse_hdr[i], + &mergedlist, &mergedsize); + if (ret < 0) + goto out_free; + /* put merged list in list index/buffer 0 */ + free(parse_hdr[0]->entries); + parse_hdr[0]->entries = mergedlist; + parse_hdr[0]->te_cnt = mergedsize; + parse_hdr[0]->te_max = mergedsize; + /* free i. list */ + free(parse_hdr[i]->entries); + parse_hdr[i]->entries = 0; + } + + output_count = 1; // now we have one big list + } + + /* output buffer(s) */ + if (output_count > 0 && outfile != -1) { + traces_count = trace_output_vbuf(outfile, parse_hdr, output_count, + (trace_strings_i *) strings, + (flags & TRACE_TIMEOFDAY) ? &tmref : 0, + flags, flags &TRACE_MIX_BUFFERS, + g_time_format); + } + +out_free: + // free memory + // output_count can be not equal to buffers_count if + // TRACE_MIX_BUFFERS flag specified + for (i = 0; i < output_count; i++) + free_entries_list(parse_hdr[i]); + + for (i = 0; i < buffers_count; i++) + free(parse_hdr[i]); + + free(parse_hdr); +out: + TRACEF("L >> ret=%d\n", ret); + return ret < 0 ? ret : traces_count; +} + + +// get format string by hash value +static int get_format_by_hash(const trace_strings_i *strings, const uint32_t hash, char **format, char **file) +{ + ENTRY hentry, *hep; + char hashstr[16]; + + if (strings == 0 || hash == 0) + return -EINVAL; + + snprintf(hashstr, sizeof(hashstr), "%u", hash); + hentry.key = hashstr; + hentry.data = 0; + + if (hsearch_r(hentry, FIND, &hep, (struct hsearch_data *) &(strings->htab)) == 0) { + TRACED("Search failed (hash:%s)\n", hentry.key); + return -ENOENT; + } + + if( format ) + *format = ((sf_entry_t *) hep->data)->format_str; + if( file ) + *file = ((sf_entry_t *) hep->data)->file; + + return 0; +} + +// get format string by hash value +static int get_format_by_hashstr(const trace_strings_i *strings, char *hashstr, char **format, char **file) +{ + ENTRY hentry, *hep; + + if (strings == 0 || hashstr == 0) + return -EINVAL; + + hentry.key = hashstr; + hentry.data = 0; + + if (hsearch_r(hentry, FIND, &hep, (struct hsearch_data *) &(strings->htab)) == 0) { + TRACED("Search failed (hash:%s)\n", hentry.key); + return -ENOENT; + } + if( format ) + *format = ((sf_entry_t *) hep->data)->format_str; + if( file ) + *file = ((sf_entry_t *) hep->data)->format_str; + + return 0; +} + +typedef union { + uint64_t u64; + uint32_t u32; + unsigned short u16; + float f32; + char *s; +} args; + + +/*! + * @brief Input data vector is assumed to be in packed form. Characters are + * treated as 1 byte, and strings are assumed to consist of string literals + * instead of char * pointers. \n + * All short (16 bit) and char(8bit) values are assumed to be type cast to 32 + * bits before they were placed into the data vector. Therefore support for + * the 'h' modifier is not included and the 'l' modifier is essentially ignored. + * To indicate a 64 bit value the 'L' modifier must be specified. + * + * Now supported : string(%s), 32 bit integers(c,d,i,o,u,x,X), 64 bit + * integers(%ll = %L with c,d,i,o,u,x,X), pointers(%p), floats(%f). + * + * Unsupported : double/long double (%lf, %Lf, %n) + * + * @param io_dest Pointer to destination buffer for the formatted string. + * @param dest_size Size of the dest buffer. + * @param i_fmt Formatting parameters to apply to the data vector. + * @param i_vparms Pointer to packed data vector to be formatted. + * @param i_swap Endian byte swap flag. + * + * @return Length of the final formatted string pointed to by io_dest. + */ +static int32_t trexMyVsnprintf(char *io_dest, size_t dest_size, + const char *i_fmt, char *i_vparms, + uint32_t i_vparms_size, uint32_t i_swap) +{ + // Local Variables + int longflag = 0; /* =1 if "L" specified */ + uint8_t len_args = 9; + args pargs[len_args]; + int argnum = 0; + const char *fmtstring = i_fmt; + char ch, *p, *vparms_end; + uint32_t uint32_empty = 0; + + uint8_t fields[len_args]; /* 64bit fields*/ + uint32_t len_sfmt[len_args]; /* massive of lens of printable elements */ + uint32_t len_fmt = 0; /* len of not formatted string */ + uint8_t i; + char tmpdata[dest_size]; /* temp data for copy */ + uint32_t prev_size = 0; /* previous size of not formatted string*/ + uint32_t prev_size_fmtd = 0; /* previous size of formatted string */ + uint32_t size = 0; /* size of formatted string */ + + memset(fields, 0, len_args); + memset(len_sfmt, 0, sizeof(uint32_t) * len_args); + + vparms_end = i_vparms + i_vparms_size - 1; + for (ch = 1; ch; ch = *i_fmt++, len_fmt++) { + if (argnum > 8) + break; + + if (ch != '%') + continue; + // skip % + ch = *i_fmt++; + len_fmt++; + // check for '%%' + if (ch == '%') { + continue; + } + // skip optional flags, search for format specifier + while (1) { + + if (ch == 'l' && i_fmt[0] == 'l') { + longflag = 1; + i_fmt++; // skip first l, second is skipped below + len_fmt++; + } else if (ch == 'L') { + longflag = 1; + } else if (!strchr("-+0123456789#lLw. 'Ihjzt", ch)) { + break; + } + ch = *i_fmt++; // skip optional char + len_fmt++; + } + + switch (ch) // diouxXeEfFgGaAcpn + { + case 's': + /* use marker if no data left in trace entry */ + len_sfmt[argnum] = len_fmt; + if (i_vparms >= vparms_end) { + pargs[argnum].s = "[[NODATA]]"; + break; + } + + /* make sure string is zero-terminated */ + p = i_vparms; + while (*p) { + if (p >= vparms_end) { + *(vparms_end) = 0; + break; + } + p++; + } + + fields[argnum] = TYPE_STRING; + if (!*i_vparms) { /* empty string */ + pargs[argnum].s = ""; + i_vparms += 4; /* word aligned */ + } else { + uint32_t tmpint; + pargs[argnum].s = i_vparms; + /* increase iv_parms by multiple of 4. we can't + * align i_vparms to a multiple of 4 as + * i_vparms isn't garanteed to be aligned */ + tmpint = strlen(i_vparms) + 1; + tmpint = (tmpint + 3) & ~3; + i_vparms += tmpint; + } + break; + case 'p': + case 'c': + case 'd': + case 'i': + case 'o': + case 'u': + case 'x': + case 'X': + len_sfmt[argnum] = len_fmt; + if (i_vparms > vparms_end) { + pargs[argnum].u32 = uint32_empty; + } else { + if (longflag) { + pargs[argnum].u64 = *(uint64_t *) i_vparms; + i_vparms += sizeof(uint64_t); + /* Do endian swap if neccessary. */ + if (i_swap) + pargs[argnum].u64 = bswap_64(pargs[argnum].u64); + fields[argnum] = TYPE_UINT64; + longflag = 0; + } else { + pargs[argnum].u32 = *(uint32_t *) i_vparms; + i_vparms += sizeof(uint32_t); + /* Do endian swap if neccessary. */ + if (i_swap) + pargs[argnum].u32 = bswap_32(pargs[argnum].u32); + } + } + break; + case 'e': + case 'f': + case 'E': + case 'F': + case 'g': + case 'G': + case 'a': + case 'A': + if (longflag) { + TRACEE("unsupported double/long-double value in trace found: %s\n", + fmtstring); + goto out; + } + + len_sfmt[argnum] = len_fmt; + pargs[argnum].f32 = *(float*) i_vparms; + i_vparms += sizeof(float); + + fields[argnum] = TYPE_FLOAT; + + if (i_swap){ + pargs[argnum].f32 = bswap_32(pargs[argnum].f32); + } + + break; + default: + TRACEE("unsupported format specifier in trace found: %c\n", + ch); + goto out; + } // switch(ch) between % and fmt + argnum++; + } /* End of for loop */ + + /* + * We go on arguments and fill it with 32/64 bit + * elements after we add tail. + */ + for (i = 0; i < argnum; i++) { + memset(tmpdata, 0, dest_size); + memcpy(tmpdata, &fmtstring[prev_size], len_sfmt[i] - prev_size); + + if (fields[i] == TYPE_UINT64) { + size = snprintf(NULL, 0, tmpdata, pargs[i].u64); + if ((prev_size_fmtd + size + 1) > dest_size) { + snprintf(&io_dest[prev_size_fmtd], + dest_size - prev_size_fmtd, tmpdata, + pargs[i].u64); + goto out; + } + snprintf(&io_dest[prev_size_fmtd], size + 1, tmpdata, pargs[i].u64); + } else if (fields[i] == TYPE_FLOAT) { + size = snprintf(NULL, 0, tmpdata, pargs[i].f32); + if ((prev_size_fmtd + size + 1) > dest_size) { + snprintf(&io_dest[prev_size_fmtd], + dest_size - prev_size_fmtd, tmpdata, + pargs[i].f32); + goto out; + } + snprintf(&io_dest[prev_size_fmtd], size + 1, tmpdata, pargs[i].f32); + } else if (fields[i] == TYPE_STRING) { + /* pointer size/value is different for x86/x86_64 */ + if (__WORDSIZE == 32) { + size = snprintf(NULL, 0, tmpdata, pargs[i].u32); + if ((prev_size_fmtd + size + 1) > dest_size) { + snprintf(&io_dest[prev_size_fmtd], + dest_size - prev_size_fmtd, tmpdata, + pargs[i].u32); + goto out; + } + snprintf(&io_dest[prev_size_fmtd], size + 1, tmpdata, pargs[i].u32); + } else { + size = snprintf(NULL, 0, tmpdata, pargs[i].u64); + if ((prev_size_fmtd + size + 1) > dest_size) { + snprintf(&io_dest[prev_size_fmtd], + dest_size - prev_size_fmtd, tmpdata, + pargs[i].u64); + goto out; + } + snprintf(&io_dest[prev_size_fmtd], size + 1, tmpdata, pargs[i].u64); + } + } else { + size = snprintf(NULL, 0, tmpdata, pargs[i].u32); + if ((prev_size_fmtd + size + 1) > dest_size) { + snprintf(&io_dest[prev_size_fmtd], + dest_size - prev_size_fmtd, tmpdata, + pargs[i].u32); + goto out; + } + snprintf(&io_dest[prev_size_fmtd], size + 1, tmpdata, pargs[i].u32); + } + prev_size_fmtd += size; + prev_size = len_sfmt[i]; + } + + memset(tmpdata, 0, dest_size); + memcpy(tmpdata, &fmtstring[prev_size], dest_size - prev_size); + + size = snprintf(NULL, 0, tmpdata); + if ((prev_size_fmtd + size + 1) > dest_size) { + snprintf(&io_dest[prev_size_fmtd], dest_size - prev_size_fmtd, tmpdata); + goto out; + } + + snprintf(&io_dest[prev_size_fmtd], dest_size - prev_size, tmpdata); + +out: + return (strlen(io_dest)); +} + + + +/*! + * @brief Format some data as hex values + * + * @param destbuf where to write the hex string to + * @param destbuflen sizeof destination buffer + * @param srcbuf pointer to data to format + * @param srcbuflen amount of data to format + * + * @return <0 for error, 0 else + */ +static int data_to_hexstring(char *destbuf, size_t destbuflen, + const char *srcbuf, size_t srcbuflen, int fl_order) +{ + uint32_t l_counter = 0; + uint32_t l_written; + uint32_t l_itr = 0; + int32_t i; + const char spaces[] = " "; // 50 SPC + + if (destbuf == 0 || destbuflen == 0) + return -EINVAL; + + if (srcbuflen == 0) { + *destbuf = 0; + return -ENOENT; + } + + while (l_counter < srcbuflen) { + // check avaiable space in buffer + if (l_itr + 10 + 40 + 1 + 16 + 2 > destbuflen) { + fprintf(stderr, "data_to_hexstring: buffer too small (%d)\n", l_counter); + return -E2BIG; + } + // Display 16 bytes in Hex with 1 space in between + l_written = 0; + if (fl_order) + l_written += sprintf(&destbuf[l_itr], "~[0x%04X] ", l_counter); + l_itr += 10; + for (i = 0; i < 16 && l_counter < srcbuflen; i++) { + l_written += + sprintf(&destbuf[l_itr], "%02X", (unsigned char) srcbuf[l_counter]); + l_itr += 2; + l_counter++; + + if (!(l_counter % 4)) { + l_written += sprintf(&destbuf[l_itr], " "); + l_itr += 1; + } + } + + // Padd with spaces + sprintf(&destbuf[l_itr], "%s", spaces + l_written); // fill to 40 chars + l_itr += (50 - l_written); + + // Display ASCII + l_written = 0; + sprintf(&destbuf[l_itr++], "*"); + for (; i > 0; i--) { + //TRACED("2bin_data = %s, new char = %c\n",bin_data,entry_data[l_counter-i]); + if (isprint(srcbuf[l_counter - i])) { + l_written += + sprintf(&destbuf[l_itr], "%c", + (unsigned char) srcbuf[l_counter - i]); + l_itr += 1; + } else { + l_written += sprintf(&destbuf[l_itr], "."); + l_itr += 1; + } + } + sprintf(&destbuf[l_itr], "%-s", spaces + l_written + 24); // fill to 16 chars + l_itr += 16 - l_written; + sprintf(&destbuf[l_itr], "*\n"); + l_itr += 2; + } + destbuf[l_itr - 1] = 0; + + return 0; +} + + +/*! + * @brief Trace parsing + * + * @param outbuf where to write the hex string to + * @param buf_size sizeof destination buffer + * @param trace_ent pointer to data to format + * @param strings + * @param file filename pointer from stringfile entry + * @param swap_flg to swap or not to swap... + * + * @return <0 for error, 0 else + */ +static int parse_trace_data(char *outbuf, const size_t buf_size, + const trace_entry_i_t * trace_ent, + const trace_strings_i *strings, char ** file, + const int32_t swap_flg) +{ + if (outbuf == 0 || buf_size == 0 || trace_ent == 0 || strings == 0) { + TRACEE("Invalid parameters"); + return -1; + } + + char *format = 0; + char *data = 0; + uint32_t len = 0; + int ret = 0; + /* TODO : get rid of this factor '5', need to compute correctly for + trexMyVsnprintf, data_to_hexstring. + */ + const int data_size = trace_ent->head.length * 5 + TRACE_MAX_BUF_SIZE; + int i; + int written; + + trace_entry_head_t *head = (trace_entry_head_t *) & (trace_ent->head); + /* set a default */ + if (file) *file = "--no-file-info--"; + + + data = (char *) calloc(1, data_size ); + if( data == 0 ) + { + TRACEPE("Cannot allocate memory"); + return -ENOMEM; + } + + switch (head->tag) { + case TRACE_COMP_TRACE: // full printf style trace: 0x434f + if (head->hash == TIMEREF_STRING) // 0x04556a8a + { + // a string with time reference information + format = "TIME REFERENCE tod=%lu.%06lus timebase: high=0x%lx low=0x%lx"; + } else if (head->hash == TIMEREF_STRING_FSP1) // 0x18afc5f5 + { + // a string with time reference and timebase information + format = + "TIME REFERENCE tod=%lu.%06lus timebase: high=0x%lx low=0x%lx freq=%luHz"; + } else { + if (get_format_by_hash(strings, head->hash, &format, file) != 0) { + TRACEI("Can't find format for hash %u (TRACE_COMP_TRACE)\n", + head->hash); + goto no_hash; + } + } + + len = trexMyVsnprintf(data, data_size, format, trace_ent->data, trace_ent->head.length, swap_flg); + if( len+1 > buf_size ) + { + TRACEE("The trace data is too big! (%d)\n", len); + ret = -E2BIG; + goto out; + } + strncpy(outbuf, data, buf_size); + + break; + + case TRACE_FIELDTRACE: // contains only 32bit values: 0x4654 + case TRACE_DEBUGTRACE: // contains only 32bit values: 0x4454 + if (get_format_by_hash(strings, head->hash, &format, file) != 0) { + TRACEI("Can't find format for hash %u (TRACE_DEBUGTRACE)\n", head->hash); + goto no_hash; + } + + len = trexMyVsnprintf(data, data_size, format, trace_ent->data, trace_ent->head.length, swap_flg); + if( len+1 > buf_size ) + { + TRACEE("The trace data is too big! (%d)\n", len); + ret = -E2BIG; + goto out; + } + strncpy(outbuf, data, buf_size); + break; + default: + case TRACE_FIELDBIN: // a binary trace of type field (non-debug): 0x4644 + case TRACE_DEBUGBIN: // a binary trace of type debug: 0x4644 + case TRACE_BINARY_TRACE: // 0x4249 + if (get_format_by_hash(strings, head->hash, &format, file) != 0) + format = 0; + + data_to_hexstring(data, data_size, trace_ent->data, trace_ent->head.length, 1); + + if (format) + snprintf(outbuf, buf_size, "%s\n%s", format, data); + else + strncpy(outbuf, data, buf_size); + break; + + case TRACE_FIELDSTRING: // a string trace of type field (non-debug): 0x4653 = "FS" + case TRACE_DEBUGSTRING: // a string trace of type debug: 0x4453 = "DS" + if (trace_ent->head.length > buf_size) { + TRACEE("The trace data is too big! (%d)\n", trace_ent->head.length); + ret = -E2BIG; + goto out; + } + /* copy string, replacing newlines */ + for (i = 0; i < trace_ent->head.length-1; i++) { + char c = trace_ent->data[i]; + if (c == 0) + break; + if (c == '\n') + c = ' '; + *outbuf++ = c; + } + /* force a terminating zero */ + *outbuf = 0; + break; + + + // a trace about droped traces: 0xFF42 + case TRACE_INTERNAL_BLOCKED: + if (head->hash != 0) { + TRACEI("Unknown hash value for TRACE_INTERNAL_BLOCKED"); + } + + format = "@@@ INTERNAL: %lu TRACES HAVE BEEN DROPPED"; + len = trexMyVsnprintf(data, data_size, format, trace_ent->data, trace_ent->head.length, swap_flg); + if( len+1 > buf_size ) + { + TRACEE("The trace data is too big! (%d)\n", len); + ret = -E2BIG; + goto out; + } + strncpy(outbuf, data, buf_size); + break; + } + goto out; + +no_hash: + data_to_hexstring(data, data_size, trace_ent->data, + trace_ent->head.length, 1); + written = snprintf(outbuf, buf_size - 2, + "!!! NO STRING NO TRACE !!! for hash=%u", head->hash); + if (trace_ent->head.length) { + strcpy(outbuf + written, "\n"); + /* don't want to fill whole big buffer, don't use strncpy */ + strncat(outbuf + written + 1, data, + buf_size - written); + } + +out: + free(data); + return ret; +} + +/* try to get endianess of pipe file. + * we need to check the "stamp" of the first trace entry. + * the first byte of the timestamp would give us a hint, but it's not really + * reliable. Instead we check the "tid" field. Linux uses 16bit PIDs, a PID + * bigger than 2^16 means we need to swap. + * returns 1 if byte-swap is needed + * 0 if no byte-swap needed + * -1 on error + */ +int get_pipe_swap_heuristic(int fd) +{ + char buf[TRACE_MAX_COMP_NAME_SIZE+sizeof(trace_entry_stamp_t)+1]; + trace_entry_stamp_t *stamp; + char *p; + int pos = lseek(fd, 0, SEEK_CUR); + int ret; + + /* read start of file with first component name and entry stamp */ + ret = read(fd, buf, sizeof(buf)); + /* rewind to old position */ + pos = lseek(fd, pos, SEEK_CUR); + + if (ret < (int) sizeof(buf)) { + /* a file smaller than buf doesn't contain a full trace entry, + * no need to check */ + return -1; + } + /* look for start of stamp, skip buf name */ + for(p = buf; (p <= buf+TRACE_MAX_COMP_NAME_SIZE) && *p; p++) ; + if (*p) { + /* end of component name not found */ + return -1; + } + + stamp = (void *) (p+1); + if (TRACE_TID_TID(stamp->tid) <= 0xffff) { + /* a valid PID. no swap necessary */ + return 0; + } + if (TRACE_TID_TID(bswap_32(stamp->tid)) <= 0xffff) { + /* PID is valid if we endian-swap it */ + return 1; + } + /* PID not valid, maybe we can guess from timestamp: + * MSB should be zero (FSP would have to run 13.7 years!) */ + if (*(p+1) != 0) { + /* first stamp bytes is LSB of second long, must swap */ + return 1; + } + /* don't know which endian we have */ + return -1; +} + +/*! + * @brief Reads traces from the trace device driver's pipe or a file. Writes the traces to the file descriptor outfile + * either binary for formatted. The traces will be read from the pipe or the file in chunks to limit memory consumption. + * If the input for this function is a file the whole file will be read and printed. If the input is the trace + * pipe one chunk of data will be read and printed. If the trace pipe buffer isn't full yet the function will sleep + * to wait for the buffer to fill. If the next chunk of data should be read from the pipe the function has to be called again. + * This way the user of this function can handle eg. keyboard input or open a new file to keep the file size below a limit. + * If fd contains -1 traces are read from the trace pipe. If fd contains a valid file descriptor traces are read from + * this file. The file should have been created with this function with the TRACE_BINARY flag set. + * + * @param outfile Is a file descriptor where the traces should be written to using the "write(2)" system call. If out- + * file is "-1" no output is generated. This can be used to check if a buffer is valid and to look for timref values + * from a TIMEREF trace. + * @param strings Has to point to a trace_string_t structure that holds information about trace format strings. This struc- + * ture can be created and filled with trace_adal_read_stringfile(). strings is ignored if the TRACE_BINARY + * flag is set. + * @param timeref Has to contain a pointer to a trace_timeref_t structure (cf. Section 1.5.4.2, "Trace with time reference + * and timebase information") if one of the flags TRACE_TIMEOFDAY and TRACE_SET_TIMEOFDAY is set. + * This structure contains a pair of time values and the timestamp frequency. These are used to translate the + * timestamp of the traces into timeofday values. If the timeref is 0 timestamp translation is only possible if a + * trace buffer contains a TIMEREF trace entry. Traces read and formatted prior to reading this trace entry are + * shown with untranslated timestamps. + * @param flags Defines the output. It is the "OR"'ed value of some of the following flags: + * - TRACE_TIME_????? Specifies the format of the timestamp value of the traces. See time_flg + * for the possible time flags. At least one of these has to be given for version 2 of the trace pipe. + * For pipe versions 3 and above this flag will not be needed and might even be ignored. + * - TRACE_BINARY The traces read from the pipe are not formatted and written in binary format to the file. + * - TRACE_PREPEND_BUFFERNAME, TRACE_APPEND_BUFFERNAME The trace pipe always can contain traces from different trace buffers, + * trace_adal_print_pipe works always in TRACE_MIX_BUFFERS mode. One of these two flags should be given + * to show the buffer a trace was written to (which will correspond to the component that issued the trace). + * Ignored if TRACE_BINARY is set. + * - TRACE_TIMEOFDAY When set timestamps are translated to timeofday values (date/time). This + * needs timeref to be given. If timeref is not given the timestamps are + * treated as if the PPC timebase counter was started at epoch time. Ignored if TRACE_BINARY is set. + * - TRACE_SET_TIMEOFDAY If a TIMEREF trace is found in a trace buffer and timeref is a valid + * pointer the values from the TIMEREF trace are written to timeref.This flag is independent of TRACE_TIMEOFDAY. + * - TRACE_FILENAME Show the name of the source file that contains the trace statement for + * each trace. Ignored if TRACE_BINARY is set. + * - TRACE_VERBOSE When this is set some messages are printed to STDERR. The messages + * eg. tell about the source for the traces (file/pipe), number of traces read etc. + * There is no formal definition of these messages. + * - TRACE_DONTSTOP When set the trace pipe isn't turned off after processing the buffer. + * Uses static memory, function isn't re-entrant with this option. + * @ return on success the number of traces written is returned. On failure a value <0 is returned. + */ +int trace_adal_print_pipe(int fd, int outfile, const trace_strings_t strings, + trace_timeref_t * timeref, int flags) +{ + /* chunksize: should be bigger than max entry size + * but this is a load-time config option of the device driver + */ + size_t chunksize = FSP_PIPE_CHUNK_SIZE; + + int pipefd = -1; // pipe fd + static int saved_pipefd=-1; // pipefd from last call w/ TRACE_DONTSTOP flag + int outfile_dup; + int readed; // bytes count + char *tracebuf = 0; // buffer for chunk + char *ptrace; // temp pointer + char *curpos; // pointer to current position in buffer + int total_entries = 0; // entries count + trace_entry_i_t ent; // trace entry + unsigned int partsize; // used for partially read support + unsigned int readsize; // used for partially read support + unsigned long foffset; + FILE *fp; + + int ret = 0; + char *fl_on = "1", *fl_off = "0"; + + // buffers for output formats + char head_fmt[512]; + char entry_fmt[128]; + char foot_fmt[512]; + + int fl_swap = 1; // TODO: how to determine??? + int fl_pipe = (fd == -1); + int fl_time = DEFAULT_TIME_FLAG; + + int fl_timeofday_is_disabled = 0; + + trace_timeref_t tmref, *ptmref = 0; + struct stat file_stat; + + TRACEF("E\n"); + + if (!(flags & TRACE_BINARY)) { + /* fclose shouldn't close outfile, therefore we need to dup */ + outfile_dup = dup(outfile); + if (outfile_dup < 0) { + TRACEPE("dup of %d failed", outfile); + return -errno; + } + fp = fdopen(outfile_dup, "a"); + if( fp == NULL ) { + TRACEPE("fdopen of %d failed", outfile_dup); + close(outfile_dup); + return -errno; + } + } else { + fp = NULL; + } + + if (flags & TRACE_TIMEOFDAY) { + if( timeref != 0 ) { + memcpy(&tmref, timeref, sizeof(trace_timeref_t)); + ptmref = &tmref; + } else { + ret = convert_time( 0, 0, fl_time, 0 ); + if( ret == CONVERT_TIME_UNKNOWN ) { + TRACEE("Timeref has not given and time format unknown(%d), -k option is disabled.\n", fl_time); + flags &= ~TRACE_TIMEOFDAY; + fl_timeofday_is_disabled = 1; + } + } + } + + // get output formats for header, entries and footer + if (!(flags & TRACE_BINARY)) { + if( trace_output_get_format(fl_timeofday_is_disabled ? flags & ~TRACE_TIMEOFDAY : flags, head_fmt, sizeof(head_fmt), + entry_fmt, sizeof(entry_fmt), foot_fmt, sizeof(foot_fmt)) == -1 ) { + TRACEE("Cannot get output format\n"); + ret = -EAGAIN; + goto out; + } + } + + /* open pipe */ + if (fl_pipe) { + if (saved_pipefd >= 0) { + /* is there an open and active pipe from last call? */ + struct stat sbuf; + + if (fstat(saved_pipefd, &sbuf) < 0) { + TRACEE("pipe not open anymore, traces might be lost"); + saved_pipefd = -1; + } else { + pipefd = saved_pipefd; + } + } + /* if no open pipe (not opened or closed) open now */ + if (pipefd < 0) { + /* turn the pipe on */ + pipefd = open(TRACE_PIPE_NAME, O_RDWR); + if (pipefd < 0) { + ret = -errno; + TRACEPE("Can't open %s", TRACE_PIPE_NAME); + goto out; + } + if (write(pipefd, fl_on, strlen(fl_on)) < 0) { + ret = -errno; + TRACEPE("Failed to turn on trace daemon"); + close(pipefd); + goto out; + } + TRACEI("Pipe %s opened.\n", TRACE_PIPE_NAME); + saved_pipefd = pipefd; + } + fl_swap = 0; + } else { + pipefd = fd; + fl_swap = get_pipe_swap_heuristic(pipefd); + if (fl_swap < 0) { + TRACEE("Cannot get endianess of pipe file, assuming big endian\n"); + fl_swap = !is_big_endian(); + } + TRACEI("Read from pipe file.\n"); + } + + + // allocate buffer for chunk + tracebuf = (char *) malloc(chunksize); + if (!tracebuf) { + ret = -ENOMEM; + TRACEPE("malloc failed for binary buffer size %zu", chunksize); + goto out_close; + } + // output header + if (!fl_pipe && !(flags & TRACE_BINARY)) { + ret = trace_output(fp, head_fmt, "tracBINARY"); + if (ret < 0) + goto out_close; + + // make sure we read at beginning (skip version byte) + lseek(pipefd, 1, SEEK_SET); + foffset = 1; + } else { + // make sure we read at beginning + lseek(pipefd, 0, SEEK_SET); + foffset = 0; + } + + partsize = 0; + readsize = chunksize; + + // read pipe by chunks + // partsize - size of previous unparsed part, if last entry is read partially, else = 0 + while ((readed = read(pipefd, tracebuf + partsize, readsize)) > 0) { + TRACEI("Got from pipe %d bytes (foffset=%lx) (left=%u)\n", + readed, foffset, partsize); + if (flags & TRACE_BINARY) { + foffset += readed; + ret = write(outfile, tracebuf, readed); + if (ret < 0) { + ret = -errno; + goto out_close; + } + fdatasync(outfile); + ret = fstat(outfile, &file_stat); + if (ret < 0) { + ret = -errno; + goto out_close; + } + posix_fadvise(outfile, 0, file_stat.st_size, POSIX_FADV_DONTNEED); + if (fl_pipe) + break; + continue; + } + /* we actually have more bytes in buffer than just read */ + readsize = readed + partsize; + ptrace = curpos = tracebuf; + + /* read and print trace by trace */ + while ((ptrace = + get_pipe_trace(ptrace, readsize - (ptrace - tracebuf), + &ent, fl_swap)) != 0 && ptrace <= tracebuf + readsize) { + print_trace_struct(&ent); // debug only + foffset += ptrace - curpos; + + TRACEV("Pipe trace num: %d pos:0x%lX(%ld) left=%td\n", + total_entries, foffset, foffset, readsize - (ptrace - tracebuf) ); + // locate timeref trace + if (!ptmref && (locate_timeref(&ent, &tmref, fl_swap) == 1)) { + ptmref = &tmref; + // if TRACE_TIMEOFDAY flag was turned off before so restore it and assign a new entry format + if( fl_timeofday_is_disabled ) { + flags &= TRACE_TIMEOFDAY; + if( trace_output_get_format(flags, 0, 0, entry_fmt, sizeof(entry_fmt), 0, 0) == -1 ) { + TRACEE("Cannot get output format\n"); + continue; + } + fl_timeofday_is_disabled = 0; + } + + if( flags & TRACE_SET_TIMEOFDAY && timeref) { + memcpy( timeref, &tmref, sizeof(trace_timeref_t)); + } + } + + // save current position + curpos = ptrace; + + // convert time to sec.usec format + ret = convert_time( &ent.stamp.tbh, &ent.stamp.tbl, fl_time, ptmref ); + + //output entry + ret = trace_output_entry(fp, entry_fmt, &ent, + (trace_strings_i *) strings, fl_swap, + flags, + g_time_format); + if (ret < 0) + goto out_close; + + total_entries++; + } + + // from the pipe just read the one chunk and return + if (fl_pipe) + break; + + // last entry readed partially? + if ((unsigned int)(curpos - tracebuf) < readsize) { + partsize = readsize - (curpos - tracebuf); + readsize = chunksize - partsize; + memmove(tracebuf, curpos, partsize); + } else { + partsize = 0; + readsize = chunksize; + } + } + if (readed < 0) { + ret = -errno; + if (errno != EINTR) + TRACEPE("Read from pipe(%d) failed", pipefd); + } + +out_close: + /* close pipe if we shouldn't keep it open */ + if (fl_pipe && !(flags & TRACE_DONTSTOP)) { + /* turn the pipe off */ + if (write(pipefd, fl_off, strlen(fl_off)) < 0) { + ret = -errno; + TRACEPE("Failed to turn off trace daemon"); + } + close(pipefd); + TRACEI("Pipe is closed.\n"); + saved_pipefd = -1; + } + +out: + if (tracebuf) + free(tracebuf); + + TRACEF("L"); + + if (fp) + fclose(fp); + return ret; +} diff --git a/adal_trace.c b/adal_trace.c new file mode 100644 index 0000000..2308d31 --- /dev/null +++ b/adal_trace.c @@ -0,0 +1,870 @@ +/* */ +/* OpenPOWER fsp-trace Project */ +/* Contributors Listed Below - COPYRIGHT 2004, 2010, 2012 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ + +/* need _GNU_SOURCE for strnlen */ +#define _GNU_SOURCE +#include <errno.h> +#include <string.h> +#include <unistd.h> +#include <stdarg.h> +#include <sys/ioctl.h> +#include <fcntl.h> + +#include "adal_common.h" +#include "trace_adal.h" + + +#define TRAC_TEMP_BUFFER_SIZE 1024 + + +static int32_t fd = -1; +static trace_desc_t td_invalid = -42; +static trace_desc_t td_uninit = -42; +extern int g_verbose_level; + + +int32_t trace_adal_init_buffer(trace_desc_t *, const char *, const size_t); + + +void __attribute__ ((constructor)) adal_trace_initialize(void) +{ + fd = open(TRACE_FULL_DEVICE_NAME, O_RDWR); + if (fd < 0) { + //fprintf(stderr, "Can't open device %d", errno); + errno = ENODEV; + return; + } + + /* private buffers for trace descriptor debug data. */ + trace_adal_init_buffer(&td_uninit, "TD_UNINIT", 4096); + trace_adal_init_buffer(&td_invalid, "TD_INVALID", 4096); +} + +void __attribute__ ((destructor)) adal_trace_exit(void) +{ + close(fd); +} + + + +static void trace_uninited_td(const trace_desc_t td, uint32_t l, uint32_t hash) +{ + trace_entry_t entry; + + entry.h.tag = TRACE_FIELDSTRING; + entry.h.line = l; + entry.h.hash = 0; + entry.h.length = sprintf((char *)entry.args, "td:%d hash:%u", td, hash); + trace_adal_write(td_uninit, sizeof(trace_entry_head_t) + entry.h.length, TRACE_FIELD, (void *) &entry); +} + + +static int32_t trac_check_size(int32_t i_size, uint16_t i_cur_length) +{ + int32_t rc = 0; + + if ((uint32_t)(i_cur_length + i_size) <= ((uint32_t)TRAC_TEMP_BUFFER_SIZE - sizeof(trace_entry_head_t))) + { + rc = 0; + } + else + { + //printf("We hit an error!\n"); + errno = EMSGSIZE; + rc = -1; + } + + return rc; +} + + + +/** + * trace_adal_init_buffer - create a tracebuffer and/or get a descriptor for it + * @o_td: the descriptor for the buffer will be written to *o_td + * @comp: the name of the buffer that should be created/looked up + * @size: the size of the buffer, ignored if buffer exists + */ + +int32_t trace_adal_init_buffer(trace_desc_t * o_td, const char *comp, const size_t size) +{ + int32_t rc = 0; + int32_t ret2 = 0; + char name[TRACE_MAX_COMP_NAME_SIZE]; // must point to 16 char byte name + trace_set_buffer_t set_buffer; + + *o_td = -1; // default to invalid + memset(name, 0, TRACE_MAX_COMP_NAME_SIZE); + + if (strlen(comp) > (TRACE_MAX_COMP_NAME_SIZE - 1)) { + rc = TRACE_INIT_BUFF_NAME_ERR; + strcpy(name,"BADN"); + } else { + strcpy(name, comp); + } + + toupper_string(name); + set_buffer.comp = name; + set_buffer.size = size; + set_buffer.td = o_td; + ret2 = ioctl(fd, TRACE_SET_BUFFER, &set_buffer); + + if (ret2 < 0) { + /* report first error if there was one */ + if (!rc) rc = TRACE_INIT_BUFF_IOCTL_ERR; + } + + if (*o_td <= 0) { + /* TD idx is DEFAULT or non-existant. */ + char data_buffer[TRAC_TEMP_BUFFER_SIZE]; + trace_entry_head_t *entry = (trace_entry_head_t *)data_buffer; + entry->tag = TRACE_FIELDSTRING; + entry->length = sprintf((char *)entry->args, + "ret:%d ret2:%d o_td:%d name:%s ", + rc, ret2, *o_td, set_buffer.comp); + trace_adal_write(td_invalid, + sizeof(trace_entry_head_t) + entry->length, + TRACE_FIELD, (void *)entry); + } + + return rc; +} + + + +/*! + * @brief Let device driver know that you are done tracing for this comp. + * + * @param td Free this trace descriptor + * + * @return Always return 0 + */ +int32_t trace_adal_free(trace_desc_t * io_td) +{ + int32_t ret = 0; + + /* set TD to default TD */ + *io_td = TRACE_DEFAULT_TD; + + return (ret); + +} + +static int32_t set_config(const trace_desc_t td, const char *name, + int32_t level, int ioctl_cmd) +{ + struct trace_set_config set_config; + int ret; + + set_config.newlevel = level; + if (name != NULL) { + set_config.u.name = name; + } else { + set_config.u.td = td; + } + + ret = ioctl(fd, ioctl_cmd, &set_config); + if (ret < 0) { + ret = TRACE_SETDEBUG_IOCTL_ERR; + } + return ret; +} + + +int32_t trace_adal_set_threshold(const int32_t level) +{ + return ioctl(fd, TRACE_SET_THRESHOLD, level); +} + + +/*! + * @brief Turn off/on components debug traces + * + * @param td Assigned trace descriptor. + * @param level If 0 only field traces will be active. If > 0 debug traces + * with level <= 'level' will be active. + * + * @return 0 for success, negative value for failure. + * @retval #TRACE_SETDEBUG_IOCTL_ERR error from device driver, errno set + * @retval #TRACE_SETDEBUG_INV_PARM_ERR second parm must be TRACE_DEBUG_ON or TRACE_DEBUG_OFF + */ +int32_t trace_adal_setdebug(const trace_desc_t td, const int32_t level) +{ + return set_config(td, 0, level, TRACE_SET_DEBUG); +} + + +/*! + * @brief Set console level for the trace buffer specified by td + * + * @param td Assigned trace descriptor. + * @param level If -1 no traces will be written to console. If 0 only field + * traces will be written to console. If > 0 debug traces + * with level <= 'level' will be written to console too. + * Only active debug traces will be shown on console + * (cf. trace_adal_setdebug). + * + * @return 0 for success, negative value for failure. + * @retval #TRACE_SETDEBUG_IOCTL_ERR error from device driver, errno set + * @retval #TRACE_SETDEBUG_INV_PARM_ERR second parm must be TRACE_DEBUG_ON or TRACE_DEBUG_OFF + */ +int32_t trace_adal_setconsole(const trace_desc_t td, const int32_t level) +{ + return set_config(td, 0, level, TRACE_SET_CONSOLE); +} + + +/*! + * @brief Set pipe level for the trace buffer specified by td + * + * @param td Assigned trace descriptor. + * @param level If -1 no traces will be written to pipe. If 0 only field + * traces will be written to pipe. If > 0 debug traces + * with level <= 'level' will be written to pipe too. + * Only active debug traces will be written to pipe + * (cf. trace_adal_setdebug). + * + * @return 0 for success, negative value for failure. + * @retval #TRACE_SETDEBUG_IOCTL_ERR error from device driver, errno set + * @retval #TRACE_SETDEBUG_INV_PARM_ERR second parm must be TRACE_DEBUG_ON or TRACE_DEBUG_OFF + */ +int32_t trace_adal_setpipe(const trace_desc_t td, const int32_t level) +{ + return set_config(td, 0, level, TRACE_SET_PIPE); +} + +/* as above, but with buffer names */ +int32_t trace_adal_setdebug_name(const char *name, const int32_t level) +{ + return set_config(0, name, level, TRACE_SET_DEBUG); +} + +int32_t trace_adal_setconsole_name(const char *name, const int32_t level) +{ + return set_config(0, name, level, TRACE_SET_CONSOLE); +} + +int32_t trace_adal_setpipe_name(const char *name, const int32_t level) +{ + return set_config(0, name, level, TRACE_SET_PIPE); +} + +/*! + * @brief Write a trace with the data given by "data" to the buffer specified by "td". + * + * @param td Assigned trace descriptor. + * @param level Debug level (0 for field trace). + * @param data Data to write to buffer. + * @param size Size of data. + * + * @return 0 for success, negative value for failure. + * @retval #TRACE_WRITE_IOCTL_ERR error from device driver, errno set + */ +int32_t trace_adal_write(const trace_desc_t i_td, const size_t i_esize, + const int32_t i_debug, const void *i_entry) +{ + trace_iovec_t do_traceiovec; + + do_traceiovec.base = i_entry; + do_traceiovec.size = i_esize; + do_traceiovec.fromuser = 1; + + return trace_adal_writev(i_td, i_debug, 1, &do_traceiovec); +} + + + +/** + * trace_adal_write2 - write a trace that consists of two data blocks + * @i_td: a trace descirptor for the buffer where the trace should be written too + * @i_debug: whether this is a field or debug trace + * @i_esize: the size of the first part of the trace entry + * @i_entry: pointer to the first part of the trace data + * @i_datasize: the size of the second part of the trace entry + * @i_data: pointer to the second part of the trace data + */ +int32_t trace_adal_write2(const trace_desc_t i_td, const int32_t i_debug, + const size_t i_esize,const void *i_entry, + const size_t i_datasize,const void *i_data) +{ + trace_iovec_t do_traceiovec[2]; + + do_traceiovec[0].base = i_entry; + do_traceiovec[0].size = i_esize; + do_traceiovec[0].fromuser = 1; + + do_traceiovec[1].base = i_data; + do_traceiovec[1].size = i_datasize; + do_traceiovec[1].fromuser = 1; + + return trace_adal_writev(i_td, i_debug, 2, do_traceiovec); +} + + +/*! + * @brief Write a trace with the data in the vector "iov" with "count" elements to the buffer + * specified by "td". + * + * @param td Assigned trace descriptor. + * @param level Debug level (0 for field trace). + * @param count Items count. + * @param iov Vector. + * + * @return 0 for success, negative value for failure. + * @retval #TRACE_WRITEV_IOCTL_ERR error from device driver, errno set + * @retval #TRACE_WRITEV_NOT_INIT trace device isn't opened, call trace_adal_init_buffer before + */ +int32_t trace_adal_writev(const trace_desc_t td, const int32_t level, + const size_t count, const struct trace_iovec * iov) +{ + int32_t ret = 0; + trace_do_tracev_t do_tracev; + + do_tracev.td = td; + // translate fsp-trace-1 debug constants to fsp-trace-2 debug level + do_tracev.level = level; + do_tracev.size = count * sizeof(trace_iovec_t); + do_tracev.iovec = iov; // must be ptr to the iovec strct. + + //printf("base[%x] size[%d] fromuser[%d]\n", iov->base, iov->size, iov->fromuser); + //print_dump(iov->base, iov->size); + ret = ioctl(fd, TRACE_DO_TRACEV, &do_tracev); + if (ret < 0) { + ret = TRACE_WRITE_IOCTL_ERR; + } + //printf("ioctl(fd:%d, TRACE_DO_TRACEV, do_tracev:%x): ret[%d] td[%d] level[%d] size[%d]\n", fd, (uint32_t)&do_tracev, ret, do_tracev.td, do_tracev.level, do_tracev.size); + + + return (ret); +} + + +/** + * trace_adal_getbufs - get list of registered trace buffers + * @i_lsize: size of buffer for list + * @o_listp: memory area to write trace buffer list to + * Description: Reads list of trace buffers. Writes a list of trace_buf_list_t + * entries. Returns the number of available trace buffers. If more + * than i_lsize buffers are available only this amount of entries + * are written to o_listp. It is valid to call with i_lsize=0 to + * the number of available buffers. + */ +int32_t trace_adal_getbufs(const size_t i_lsize, trace_buf_list_t * o_listp) +{ + int32_t ret = 0; + trace_getbufs_t getbufs; + + TRACEF("E\n"); + + getbufs.size = i_lsize; + getbufs.list = o_listp; + + ret = ioctl(fd, TRACE_GET_BUFNAMES, &getbufs); + if (ret < 0) { + TRACEPE("ioctl(TRACE_GET_BUFNAMES) failed"); + ret = TRACE_GETBUFS_IOCTL_ERR; + } + + TRACEF("L\n"); + return ret; +} + + + +/** + * trace_adal_getbufs - get list of registered trace buffers + * @i_comp: name of a trace buffer + * Description: Deletes a trace buffer. + */ +int32_t trace_adal_delete_buffer(const char *i_comp) +{ + int32_t ret = 0; + char name[16]; /* must point to 16 char byte name */ + + TRACEF("E\n"); + + strcpy(name,i_comp); + toupper_string(name); + ret = ioctl(fd,TRACE_DELETE_BUFFER,name); + if(ret < 0) { + ret = TRACE_DELETE_IOCTL_ERR; + } + + TRACEF("L\n"); + return(ret); +} + + + +/*** trace_adal_read *********************************************************** + *** trace_adal_read_differ **************************************************** + * Copy the contents of tracebuffer 'comp' to buffer 'buff' with a max size of + * 'size'. trace_read_delta, will find the difference from the last time + * trace_read_delta was run. + ******************************************************************************/ + +inline static int32_t adal_trace_read(const char * comp, const size_t size, void * buff, unsigned long cmd) +{ + int32_t rc = 0; + trace_read_buffer_t tracebuffer; + char name[16]; + + strncpy(name, comp, 16); + name[15] = 0; + toupper_string(name); + + tracebuffer.comp = name; + tracebuffer.size = size; + tracebuffer.data = buff; + + rc = ioctl(fd, cmd, &tracebuffer); + if (rc < 0) rc = TRACE_READ_IOCTL_ERR; + + return rc; +} + +int32_t trace_adal_read(const char * comp, const size_t size, void * buff) +{ + return adal_trace_read(comp, size, buff, TRACE_READ_BUFFER); +} + +int32_t trace_adal_read_diff(const char * comp, const size_t size, void * buff) +{ + return adal_trace_read(comp, size, buff, TRACE_READ_DELTA); +} + +int32_t trace_adal_read_recent(const char * comp, const size_t size, void * buff) +{ + return adal_trace_read(comp, size, buff, TRACE_READ_RECENT); +} + + +/** + * trace_adal_write_ints - write a trace, data consists of a number of int values (32bit) + * @i_td: a trace descirptor for the buffer where the trace should be written too + * @i_debug: whether this is a field or debug trace + * @line: source line number of trace + * @nargs: number of int values + * @hash: The hash/trace-id for this trace + * @p1: the first int value + * @p2: the second int value + * @p3: the third int value + * @p4: the fourth int value + * @p5: the fifth int value + * @p6: the sixth int value + * @p7: the seventh int value + * @p8: the eight int value + * @p9: the nineth int value + * Description: Writes a trace. Doesn't parse format string. printf args have to + * fit into an int (32bit). Number of int values has to be given. + */ +int32_t trace_adal_write_ints(const trace_desc_t i_td, const int32_t i_debug, + uint32_t line, int nargs, uint32_t hash, uint32_t p1, uint32_t p2, + uint32_t p3, uint32_t p4, uint32_t p5, uint32_t p6, uint32_t p7, + uint32_t p8, uint32_t p9) +{ + return trace_adal_write_ints9(i_td, ((i_debug & 0xff) << 24) | ((nargs & 0xff) << 16) | line, + hash, p1, p2, p3, p4, p5, p6, p7,p8, p9); +} + +/* function for 0..5 argument traces. on PPC upto 8 func params fit in + * registers, this function doesn't need to put params on the stack. + * uses ioctl directly (not adal) to reduce number of calls + */ +int32_t trace_adal_write_ints5(const trace_desc_t i_td, const uint32_t i_dln, + uint32_t hash, uint32_t p1, uint32_t p2, + uint32_t p3, uint32_t p4, uint32_t p5) +{ + trace_do_tracev_t do_tracev; + trace_iovec_t do_traceiovec; + trace_entry_t entry; + int32_t ret = 0; + union { uint32_t u; + struct { uint8_t tag, nargs; uint16_t line; } s; + } opt; + + opt.u = i_dln; + entry.h.tag = opt.s.tag ? TRACE_DEBUGTRACE : TRACE_FIELDTRACE; + entry.h.line = opt.s.line; + entry.h.length = sizeof(uint32_t) * opt.s.nargs; + entry.h.hash = hash; + switch(opt.s.nargs) { + case 5: entry.args[4]= p5; /*fall through*/ + case 4: entry.args[3]= p4; /*fall through*/ + case 3: entry.args[2]= p3; /*fall through*/ + case 2: entry.args[1]= p2; /*fall through*/ + case 1: entry.args[0]= p1; /*fall through*/ + default: ; + } + + if (i_td <= 0) { + trace_uninited_td(i_td, opt.s.line, hash); + } + + do_tracev.td = i_td; + do_tracev.level = opt.s.tag; + do_tracev.size = sizeof(trace_iovec_t); // unless more than one + do_tracev.iovec = &do_traceiovec; // must be ptr to the iovec strct. + + do_traceiovec.base = (void *) &entry; + do_traceiovec.size = sizeof(trace_entry_head_t) + entry.h.length; + do_traceiovec.fromuser = 1; + + /* we will check validity of trace descriptor in device driver */ + ret = ioctl(fd,TRACE_DO_TRACEV,&do_tracev); + if(ret < 0) + { + ret = TRACE_WRITE_IOCTL_ERR; + } + + return(ret); +} + +/* 9 parameter version version of write_ints. the last 4 will be put on the + * stack which makes this function more expensive (slower) + */ +int32_t trace_adal_write_ints9(const trace_desc_t i_td, const uint32_t i_dln, + uint32_t hash, uint32_t p1, uint32_t p2, + uint32_t p3, uint32_t p4, uint32_t p5, uint32_t p6, uint32_t p7, + uint32_t p8, uint32_t p9) +{ + trace_do_tracev_t do_tracev; + trace_iovec_t do_traceiovec; + trace_entry_t entry; + int32_t ret = 0; + union { uint32_t u; + struct { uint8_t tag, nargs; uint16_t line; } s; + } opt; + + opt.u = i_dln; + entry.h.tag = opt.s.tag ? TRACE_DEBUGTRACE : TRACE_FIELDTRACE; + entry.h.line = opt.s.line; + entry.h.length = sizeof(uint32_t) * opt.s.nargs; + entry.h.hash = hash; + switch(opt.s.nargs) { + case 9: entry.args[8]= p9; /*fall through*/ + case 8: entry.args[7]= p8; /*fall through*/ + case 7: entry.args[6]= p7; /*fall through*/ + case 6: entry.args[5]= p6; /*fall through*/ + case 5: entry.args[4]= p5; /*fall through*/ + case 4: entry.args[3]= p4; /*fall through*/ + case 3: entry.args[2]= p3; /*fall through*/ + case 2: entry.args[1]= p2; /*fall through*/ + case 1: entry.args[0]= p1; /*fall through*/ + default: ; + } + + if (i_td <= 0) { + trace_uninited_td(i_td, opt.s.line, hash); + } + + do_tracev.td = i_td; + do_tracev.level = opt.s.tag; + do_tracev.size = sizeof(trace_iovec_t); // unless more than one + do_tracev.iovec = &do_traceiovec; // must be ptr to the iovec strct. + + do_traceiovec.base = (void *) &entry; + do_traceiovec.size = sizeof(trace_entry_head_t) + entry.h.length; + do_traceiovec.fromuser = 1; + + /* we will check validity of trace descriptor in device driver */ + ret = ioctl(fd,TRACE_DO_TRACEV,&do_tracev); + if(ret < 0) + { + ret = TRACE_WRITE_IOCTL_ERR; + } + + return(ret); +} + + +/** + * trace_adal_write_all - write a trace, parsing the format string for data count and types + * @i_td: a trace descirptor for the buffer where the trace should be written too + * @i_hash: The hash/trace-id for this trace + * @i_fmt: the printf format string + * @i_line: source line number of trace + * @i_type: whether this is a field or debug trace + * Description: Writes a trace. Parses the format string for % format specifiers to get + * the number and types of parameters. Supports %d,%u,%p,%x and %s. + */ +int32_t trace_adal_write_all(const trace_desc_t i_td,const trace_hash_val i_hash, + const char *i_fmt, + const uint32_t i_line, const int32_t i_type,...) +{ + va_list ap; + uint8_t ch; + uint32_t tmpuint; + uint64_t tmpulong; + int32_t longflag = 0; /* =1 if ulonglong of long double (ll/L) */ + int32_t precision = 0; /* Format precision */ + int32_t l_cast = 0, len; + char *tmpcharptr = NULL; + char data_buffer[TRAC_TEMP_BUFFER_SIZE]; + trace_entry_head_t *entry = (trace_entry_head_t *)data_buffer; + char *wptr = (char *) (entry+1); + int32_t ret = 0; + trace_iovec_t do_traceiovec; + const char *fmt_start = i_fmt; + int32_t counter = 0; + + if (i_td <= 0) { + trace_uninited_td(i_td, i_line, i_hash); + return TRACE_WRITE_ALL_BAD_TD; + } + + memset(entry,0,TRAC_TEMP_BUFFER_SIZE); + + entry->tag = TRACE_COMP_TRACE; + entry->line = i_line; + entry->length = 0; + entry->hash = i_hash; + + va_start(ap, i_type); + for (;;) + { + switch (ch = *i_fmt++) + { + case 0: + goto out; + break; + case '%': + /* Increment past the % */ + ch = *i_fmt++; + + if((ch == '-') || (ch == '+')) + { + /* ignore left/right allignment */ + ch = *i_fmt++; + } + + /* Handle width for hex */ + for (; ch >= '0' && ch <= '9'; ) + { + ch = *i_fmt++; + } /* End for */ + + /* Skip "." and get precision */ + precision = 0; + if (ch == '.') + { + ch = *i_fmt++; + for (precision=0; ch >= '0' && ch <= '9'; ) + { + precision = (precision * 10) + (ch - '0'); + ch = *i_fmt++; + } /* End for */ + } + if (ch == '#') + { + // ignore # formatting + ch = *i_fmt++; + } + + /* check for "z" */ + if (ch == 'z') + { + // handle '%zu' same as '%u' so just ignore the z + ch = *i_fmt++; + } + + /* Check for "l" */ + if (ch == 'l') + { + /* all 16 bit values will be cast to 32 bit by the trace + * functions so the 'l' flag is redundant. + * Check for second l (ll => 64bit) */ + ch = *i_fmt++; + if (ch == 'l') { + // ll -> 64bit + longflag = 1; + ch = *i_fmt++; + } + } + + /* Check for "L"/"j"/"q" (64bit int or long double) */ + if (ch == 'L' || ch == 'j' || ch == 'q') + { + longflag = 1; + ch = *i_fmt++; + } + + /* Check for "w" */ + if (ch == 'w') + { + ch = *i_fmt++; + } + switch (ch) + { + case 'c': + counter++; + l_cast = va_arg(ap,int32_t); + if (trac_check_size(4, entry->length) == 0) { + memcpy(wptr, &l_cast, 4); + wptr += 4; + entry->length += 4; + } else { + ret = -E2BIG; + goto error; + } + break; + case 's': + counter++; + tmpcharptr = va_arg(ap, char *); + if (tmpcharptr == NULL) { + /* put "NUL" into buffer (only 4 bytes!) */ + if(trac_check_size(4, entry->length) == 0) { + memcpy(wptr, "NUL", 4); + wptr += 4; + entry->length += 4; + } else { + /* buffer full, not even 4 bytes fit anymore! */ + ret = -E2BIG; + goto error; + } + break; + } + len = strnlen(tmpcharptr, precision ? precision + : TRAC_TEMP_BUFFER_SIZE); + if (trac_check_size(len+1, entry->length) == 0) { + memcpy(wptr, tmpcharptr, len); + /* manually add terminating zero in case + * precision given (%.4s, no 0 available) */ + wptr[len++] = 0; + /* data size needs to be a multiple of four, + * fill with 0 */ + while(len & 3) + wptr[len++] = 0; + wptr += len; + entry->length += len; + } else if (trac_check_size(4, entry->length) == 0) { + /* string is too long, store just the terminating zero */ + *(int *) wptr = 0; + wptr += 4; + entry->length += 4; + } else { + /* buffer full, not even 4 bytes fit anymore! */ + ret = -E2BIG; + goto error; + } + break; + case 'p': /* longflag not valid for p, but who cares? */ + case 'd': + case 'i': + case 'x': + case 'X': + case 'u': + case 'o': + if (longflag) + { + if(trac_check_size(sizeof(uint64_t), + entry->length) == 0) + { + counter++; + tmpulong = va_arg(ap, uint64_t); + memcpy(wptr, &tmpulong, 8); + wptr += 8; + entry->length += 8; + break; + } + } else { + if(trac_check_size(sizeof(uint32_t), + entry->length) == 0) + { + counter++; + tmpuint = va_arg(ap, uint32_t); + memcpy(wptr, &tmpuint, 4); + wptr += 4; + entry->length += 4; + break; + } + } + ret = -E2BIG; + goto error; + case 'f': + case 'F': + case 'e': + case 'E': + case 'g': + case 'G': + case 'a': + case 'A': + counter++; + /* Ignore floating point */ + if (longflag) + { + va_arg(ap, long double); + } + else + { + va_arg(ap, double); + } + break; + default: {} + } + + if (counter > MAX_ARGS) { + errno = E2BIG; + ret -= 1; + goto error; + } + break; + + } + } + + out: + /* all data is now in entry */ + do_traceiovec.base = (void *) entry; + do_traceiovec.size = sizeof(trace_entry_head_t) + entry->length; + do_traceiovec.fromuser = 1; + + ret = trace_adal_writev(i_td, i_type == TRACE_FIELD ? TRACE_FIELD + : TRACE_DEBUG, 1, &do_traceiovec); + + goto out2; + + error: + entry->tag = TRACE_FIELDSTRING; + entry->line = i_line; + entry->hash = 0; + if (counter > MAX_ARGS) { + entry->length = sprintf((char *) entry->args, ">>TOO MANY ARGS (max args %i, hash %u)<<", + MAX_ARGS, i_hash); + } else { + entry->length = sprintf((char *) entry->args, ">>TRACE TOO BIG (max %u, at pos %ti, hash %u)<<", + TRAC_TEMP_BUFFER_SIZE, i_fmt-fmt_start, i_hash); + } + trace_adal_write(i_td,(sizeof(trace_entry_head_t) + entry->length), + TRACE_FIELD, (void *)(entry)); + + out2: + return(ret); +} + + +int32_t trace_adal_clear_buffs(void) +{ + int32_t rc = 0; + + rc = ioctl(fd, TRACE_CLEAR_BUFFERS, NULL); + if (rc < 0) rc = TRACE_CLEAR_IOCTL_ERR; + + return rc; +} diff --git a/copyright.c b/copyright.c new file mode 100644 index 0000000..355c277 --- /dev/null +++ b/copyright.c @@ -0,0 +1,23 @@ +/* */ +/* OpenPOWER fsp-trace Project */ +/* Contributors Listed Below - COPYRIGHT 2004. */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ + + +static const char copyright [] __attribute__((unused)) + __attribute__((section (".comment"))) = + "Licensed under the Apache License, Version 2.0.\n"; diff --git a/fsp-trace.c b/fsp-trace.c new file mode 100644 index 0000000..5db12d5 --- /dev/null +++ b/fsp-trace.c @@ -0,0 +1,1247 @@ +/* */ +/* OpenPOWER fsp-trace Project */ +/* Contributors Listed Below - COPYRIGHT 2004, 2010. */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ + +/****************************************************************************** + * fsp-trace.c + * Contains fsp-trace post-processor. Used to fetch, parse, and format traces. + *****************************************************************************/ + + +/* Change Log *****************************************************************/ +/* */ +/* 10/26/05 Created by Artur Hisamov (artur.hisamov@auriga.ru) */ +/* End Change Log *************************************************************/ + +#include <stdlib.h> +#include <unistd.h> +#include <stdio.h> +#include <fcntl.h> +#include <error.h> +#include <argp.h> +#include <execinfo.h> /* for backtrace* */ +#include <string.h> +#include <signal.h> +#include <sys/stat.h> +#include <sys/types.h> + +#include "trace_adal.h" +#include "adal_common.h" + + +int g_process = 0; // global flag for signal handling +extern int g_verbose_level; + + +#define TRACE_DEFAULT_VERBOSE_LEVEL TRACE_LVL1 +#define DEFAULT_STRINGFILE "trexStringFile" + +const char *argp_program_version = "fsp-trace " ARCH; +const char *argp_program_bug_address = "ivan@de.ibm.com"; +error_t argp_err_exit_status = EINVAL; +char doc[] = "fsp-trace -- Used to retrieve and view fsp trace buffers"; + +static struct argp_option options[] = { + {"address", 'a', "IP", 0, "IP Address of CRP (X86 Only)", 0}, + {"binary", 'b', 0, 0, "Dump binary buffers to file (don't process it). " + "Use for timing critical situations", 0}, + {"comps", 'c', "'C1 C2 ..'", 0, "Retrieve only specified trace buffers", 0}, + {"debug", 'd', "COMP:LV", 0, "Set COMP debug trace to LV (1=on, 0=off)", 0}, + {"file_name", 'f', 0, 0, "Display File Name with each trace", 0}, + {"date/time", 'k', 0, 0, "Convert timestamps to date/time format", 0}, + {"console", 'l', "COMP:LV", 0, "Set COMP console trace to LV (1=on, 0=off)", 0}, + {"nosort", 'n', 0, 0, "Do not sort trace buffers by timestamp", 0}, + {"output_dir", 'o', "DIR/FILE", 0, "Write output to DIR/tracBINARY or DIR/tracMERG " + "or FILE (default: to stdout)", 0}, + {"process", 'p', 0, 0, "run in process mode, continuously collecting trace", 0}, + {"daemon", 'P', 0, 0, "process mode (like -p), but run in background", 0}, + {"reset", 'r', 0, 0, "Reset all trace buffers on system", 0}, + {"stringfile", 's', "StrFile", 0, "Location of trexStringFile", 0}, + {"tail", 't', "NUM", 0, "Only show last NUM traces", 0}, + {"verbose", 'v', "LV", 0, "Internal trace level for fsp-trace", 0}, + {"format", 'F', "", 0, "time format in strftime mode", 0}, + {0, 0, 0, 0, 0, 0} +}; + +const char args_doc[] = "-s <stringfile> [trac<COMP>...|tracBINARY|tracARCHIVE]"; + +struct arguments { + char *address; + int32_t binary; + char *comps[256]; + int32_t debug; + int32_t console; + int32_t filename; + int32_t date; + int32_t tformat; + int32_t nosort; + char *output_dir; + int32_t output_is_dir; + int32_t process; + int32_t reset; + int32_t tail; + int32_t verbose; + + //int32_t stringfile_ver; + uint32_t comp_count; + char *debug_comp; + char *console_comp; + int32_t file_count; + uint32_t stringfile_count; + char ** input_files; + char ** string_files; + char *input_dir; + int has_version; + int files_given; +}; + + +static int is_buffer_in_list(const char *i_file_name, char *comps[], int32_t comp_count); +static error_t parse_opt(int key, char *arg, struct argp_state *state); +static int is_tracBINARY(char *file); +static int is_smartDump(char *file); +static void sig_handler(int); + + +void _toupper_string(char *text) +{ + size_t j; + + for (j = 0; j < strlen(text); j++) { + text[j] = toupper(text[j]); + } +} + + +/* help function for parse_opt: split "COMP:LV" argument */ +static inline int parse_comp_lv(char *arg, char **pcomp, int *lv) +{ + int len; + char *p = strchr(arg, ':'), *newbuf; + + if (p == NULL) { + /* maybe old sytnax: -d BUFFER lvl */ + TRACED("old syntax for -d/-c: COMP LV w/o ':' (%s)\n", arg); + *pcomp = strdup(arg); + if (pcomp == NULL) { + TRACEE("out of memory copying comp name"); + return ENOMEM; + } + _toupper_string(*pcomp); + return EAGAIN; /* needs more data */ + } + len = (int) (p - arg); + newbuf = (char *) malloc(len + 1); + if (newbuf == NULL) { + TRACEE("out of memory copying comp name"); + return ENOMEM; + } + memcpy(newbuf, arg, len); + newbuf[len] = 0; + _toupper_string(newbuf); + *lv = atoi(p + 1); + *pcomp = newbuf; + return 0; +} + +static inline int parse_add_comp_name(struct arguments *args, char *names) +{ + char *p, *p2 = names, *comp; + int len; + + do { + if (args->comp_count >= sizeof(args->comps)/sizeof(char *)) { + TRACEE("Too many components given (max=%zu).\n", + sizeof(args->comps)/sizeof(char *)); + return ENOMEM; + } + p = strchr(p2, ' '); + len = p ? p - p2 : (int)strlen(p2); /* p==0: last value */ + + comp = (char *) malloc(len + 1); + if (comp == NULL) { + TRACEE("out of memory copying stringfile name"); + return ENOMEM; + } + memcpy(comp, p2, len); + comp[len] = 0; + _toupper_string(comp); + + args->comps[args->comp_count] = comp; + args->comp_count++; + p2 = p ? p + 1 : 0; /* p is zero in last loop run */ + TRACEV("option -c: buffer name %s\n", args->comps[args->comp_count - 1]); + } while (p != NULL); + return 0; +} + +/*! + * @brief Parse the arguments passed into fsp-trace + * + * @param key The parameter + * @param arg Argument passed to parameter + * @param state Location to put information on paameters + * + * @return tracRC_t t_rc +*/ +static error_t parse_opt(int key, char *arg, struct argp_state *state) +{ + struct stat l_statbuf; + struct arguments *args = state->input; + int ret; + static char last_option; + + if (!(key & 0xffffff00) && isprint(key)) { + TRACEV(" option '%c' found\n", key); + } + + /* this is ugly: need to maintain support for old fsp-trace syntax: + * options "-d COMP LV" "-l COMP LV" "-c COMP1 COMP2 COMP3" + */ + if (last_option) { + /* continuing one of -d/-l/-c */ + if (key == ARGP_KEY_ARG) { + /* a non-option value */ + if (last_option == 'd' || last_option == 'l') { + /* need a number, parse option */ + char *endptr; + int level; + + level = strtol(arg, &endptr, 10); + if (*endptr != 0) { + /* extra text after number */ + TRACEE("cannot read -%c level (%s)\n", + last_option, arg); + return EINVAL; + } + if (last_option == 'd') { + args->debug = level; + } else if (last_option == 'l') { + args->console = level; + } else { + TRACEE("INTERNAL ERROR: bad value for last_option (-%c)\n", + last_option); + return EINVAL; + } + TRACEV("found level %d for -%c (old syntax)\n", + level, last_option); + /* done with arg and -d/l option */ + last_option = 0; + return 0; + } + if (last_option == 'c') { + /* arg is a buffer/component name */ + /* keep last_option, there might be more names */ + return parse_add_comp_name(args, arg); + } + TRACEE("INTERNAL ERROR: last_option = %c\n", last_option); + return EINVAL; + } else if (last_option != 'c') { + /* an option or end-of-args. number for -d/-l is missing */ + TRACEE("option requires two arguments -- %c\n", last_option); + last_option = 0; + return EINVAL; + /* in -c case we fall through, disabling -c mode. + * -c without args won't be detected as an error. + * this is the price for supporting a broken interface */ + } + last_option = 0; + } + + switch (key) { + case 'k': args->date = 1; break; + case 'n': args->nosort = 1; break; + case 'f': args->filename = 1; break; + case 'p': args->process = 1; break; + case 'P': args->process = 2; break; + case 'b': args->binary = 1; break; + case 'r': args->reset = 1; break; + case 'F': + args->tformat = 1; + memcpy(g_time_format, arg, strlen(arg)); + break; + + case 'o': + args->output_dir = (char *) calloc(1, strlen(arg) + 2); + strcpy(args->output_dir, arg); + + /* if output_dir doesn't exist or isn't a directory take + * it as filename to write output to */ + ret = stat(arg, &l_statbuf); + if (ret < 0) { + args->output_is_dir = 0; + } else if (S_ISDIR(l_statbuf.st_mode)) { + args->output_is_dir = 1; + } else { + args->output_is_dir = 0; + } + TRACED(" -o is given. (output:%s %s)\n", arg, args->output_is_dir ? "(dir)" : ""); + break; + + case 's': + if (args->stringfile_count >= sizeof(args->string_files)/sizeof(char *)) { + TRACEE("too many stringfiles given (max=%zu)\n", + sizeof(args->string_files)/sizeof(char *)); + return ENOMEM; + } + args->string_files[args->stringfile_count] = strdup(arg); + if (args->string_files[args->stringfile_count] == NULL) { + TRACEE("out of memory copying stringfile name"); + return ENOMEM; + } + TRACEV(" -s is given. (stringfile:%s)\n", arg); + args->stringfile_count++; + break; + + case 'v': + args->verbose = atoi(arg); + switch (args->verbose) { + case 0: g_verbose_level = TRACE_LVL0; break; + case 1: g_verbose_level = TRACE_LVL1; break; + case 2: g_verbose_level = TRACE_LVL2; break; + case 3: g_verbose_level = TRACE_LVL3; break; + case 4: g_verbose_level = TRACE_LVL4; break; + case 5: g_verbose_level = TRACE_LVL5; break; + default: g_verbose_level = TRACE_LVL_ALL; break; + } + TRACEI("Set verbose level to %d (mask:0x%X)\n", args->verbose, g_verbose_level); + break; + + case 'd': + ret = parse_comp_lv(arg, &args->debug_comp, &args->debug); + if (ret == EAGAIN) { + last_option = 'd'; + TRACEV("found option -d buffer=%s, no level (old syntax)\n", + args->debug_comp); + } else if (ret) { + return ret; + } else { + TRACEV("found option -d buffer=%s level=%u)\n", + args->debug_comp, args->debug); + } + break; + + case 'l': + ret = parse_comp_lv(arg, &args->console_comp, &args->console); + if (ret == EAGAIN) { + last_option = 'l'; + TRACEV("found option -l buffer=%s, no level (old syntax)\n", + args->console_comp); + } else if (ret) { + return ret; + } else { + TRACEV("found option -l buffer=%s level=%u)\n", + args->console_comp, args->console); + } + break; + + case 'c': + ret = parse_add_comp_name(args, arg); + if (ret) + return ret; + /* set last_option, there might be more names */ + last_option = 'c'; + break; + + case ARGP_KEY_ARG: + TRACED("arg is given: %s\n", arg); + /* input file, pipe or dir? */ + ret = stat(arg, &l_statbuf); + if (ret < 0) { + TRACEPE("cannot find source file %s", arg); + return EINVAL; + } + + if (S_ISDIR(l_statbuf.st_mode)) { + /* directory given, look for tracBINARY */ + char *fullpath; + TRACEI("Will use tracBINARY as input in %s\n", arg); + if (args->input_dir) { + TRACEE("Two input directories not supported\n"); + return EINVAL; + } + + fullpath = (char *) malloc(strlen(arg) + + sizeof("tracBINARY") + 1); + if (fullpath == NULL) { + TRACEE("out of memory for tracBIANRY filename"); + return EINVAL; + } + sprintf(fullpath, "%s/tracBINARY", arg); + + ret = stat(fullpath, &l_statbuf); + if (ret < 0) { + TRACEPE("cannot find tracBINARY file in %s", + arg); + free(fullpath); + return EINVAL; + } + + ret = is_tracBINARY(fullpath); + if (ret == 0) { + TRACEE("File %s: bad type or version\n", fullpath); + free(fullpath); + return EINVAL; + } else if (ret < 0) { + TRACEE("File %s: cannot read or empty\n", fullpath); + free(fullpath); + return ENOENT; + } + args->input_dir = fullpath; + args->files_given++; + + } else if (S_ISREG(l_statbuf.st_mode)) { + args->files_given++; + /* a regular file. tracBINARY or raw buffer? */ + ret = is_tracBINARY(arg); + if (ret < 1) { + /* not tracBINARY or not readable/empty */ + ret = is_smartDump(arg); + if (ret < 1) { + TRACEE("file %s: not an fsp-trace file (Incorrect Version?)\n", arg); + /* don't return an error or rest of args is ignored. + * should we add to list so message gets printed to outfile? */ + return 0; /* EINVAL */ + } + } + + TRACEI("Use %s as input.\n", arg); + + args->input_files[args->file_count] = strdup(arg); + if (args->input_files[args->file_count] == NULL) { + TRACEE("out of memory for tracebuffer filename"); + return EINVAL; + } + args->file_count++; + } else { + TRACEE("Unknown Argument %s\n", arg); + return ARGP_ERR_UNKNOWN; + } + break; + + default: + return ARGP_ERR_UNKNOWN; + } + + return 0; +} + +void show_version(void) +{ + return; +} + +/*! + * @brief check if a file is a tracBINARY file + * + * done by reading first byte. this has to be 0x02 (version of tracBINARY file) + * this is a lousy test, but probably good enough + * 1 == is, 0 == is not, -1 cannot open/read/empty + */ +int is_tracBINARY(char *file) +{ + char ver; + int fd, ret; + + if (file == 0) + return 0; + + fd = open(file, O_RDONLY); + if (fd < 0) + return -1; + + do { + ret = read(fd, &ver, 1); + } while (ret < 0 && errno == EINTR); + close(fd); + if (ret <= 0) + return -1; + + if (ver != 2) + return 0; + + return 1; +} + +/*! + * @brief check if a file is a "smartDump", i.e. contains fsptrace buffers + * 1 == is, 0 == is not, -1 cannot open/read/empty + */ +int is_smartDump(char *file) +{ + trace_buf_head_t head; + int32_t rc, fd, time_flg; + + if (!file) + return -1; + + + fd = open(file, O_RDONLY); + if (fd < 0) + return -1; + + do { + rc = read(fd, &head, sizeof(trace_buf_head_t)); + } while (rc < 0 && errno == EINTR); + if (rc <= 0) { + TRACEPE("read %zu bytes of %s = %d, %d", + sizeof(trace_buf_head_t), file, rc, errno); + return -1; + } + + if (rc != sizeof(trace_buf_head_t)) { + TRACEPE("Cannot read from the file %s", file); + close(fd); + return -1; + } + + /* checking fsptrace buffer header: + * byte version: 1 + * byte header_length: sizeof(trace_buf_head_t) == 32 + * byte time_flag: 0-3 + * byte endian_flag: 'b'/'l' + */ + if (head.ver != 1) { + TRACEI("Wrong 'smartdump' header format. (ver=0x%X)\n", head.ver); + close(fd); + return 0; + } + + if ( head.hdr_len != sizeof(struct trace_buf_head_v1) && + head.hdr_len != sizeof(struct trace_buf_head_v2) ) { + TRACEI("Wrong 'smartdump' header format. (hdr_len=0x%X)\n", head.hdr_len); + close(fd); + return 0; + } + + time_flg = head.time_flg & ~TRACE_TIME_EPOCH; /* bit 7 is "epoch" marker */ + if (time_flg != TRACE_TIME_REAL && + time_flg != TRACE_TIME_50MHZ && + time_flg != TRACE_TIME_167MHZ && + time_flg != TRACE_TIME_200MHZ && + time_flg != TRACE_TIME_TIMESPEC && + head.time_flg != TRACE_TIME_UNKNOWN) { + TRACEI("Wrong 'smartdump' header format. (time_flg=0x%X)\n", head.time_flg); + close(fd); + return 0; + } + + if (head.endian_flg != 'B' && head.endian_flg != 'b' && + head.endian_flg != 'L' && head.endian_flg != 'l') { + TRACEI("Wrong 'smartdump' header format. (endian_flg=0x%X)\n", head.endian_flg); + close(fd); + return 0; + } + + close(fd); + return 1; +} + + +/*! + * @brief lookup name in list + * + * @return 1 if name is in list or list empty, 0 else +*/ +int is_buffer_in_list(const char *name, char *list[], int32_t count) +{ + int32_t i; + int ret = 0; + + TRACEF("(name=%s, count=%d\n", name, count); + if (!list || !list[0]) { + /* list empty. return 1 as empty list means "get all" */ + ret = 1; + } else { + for (i = 0; i < count; i++) { + if (list[i] != NULL && !strcasecmp(name, list[i])) { + ret = 1; + break; + } + } + } + return ret; +} + + +void sig_handler(int i_signal) +{ + + TRACEI("Received signal %d\n", i_signal); + + if (i_signal == 11) { + /* gdb sometimes doesn't print all stack-entries from core. + * backtrace() helped here. + * enable this in main() by uncommenting sighandler install. + * you might need to remove -O* from Makefile + */ + void *callstack[10]; + backtrace(callstack, 10); + backtrace_symbols_fd(callstack, 10, 2); + abort(); + } + + /* we react on all signals the same way: exit gracefully */ +#if 0 +//doesn't work: read(16384) might return 4096 instead of -1+EINTR + if (g_process) { + /* we are running as a process so let daemon close pipe and exit */ + g_process=0; + return; + } +#endif + exit(i_signal); +} + + +trace_strings_t read_stringfiles(struct arguments *args) +{ + int flags = TRACE_OVERWRITE | TRACE_VERBOSE; + trace_strings_t strings, tmp_str; + uint32_t i; + + if (args->stringfile_count == 0) { + /* no stringfile given, use default name */ + TRACEI("Using stringfile %s\n", DEFAULT_STRINGFILE); + strings = trace_adal_read_stringfile(0, DEFAULT_STRINGFILE, + flags); + if (strings == 0) { + TRACEE("cannot read stringfile '%s'\n", DEFAULT_STRINGFILE); + } + return strings; + } + strings = 0; + for (i = 0; i < args->stringfile_count; i++) { + TRACEI("Using stringfile %s\n", args->string_files[i]); + tmp_str = trace_adal_read_stringfile(strings, args->string_files[i], + flags); + if (tmp_str == 0) { + TRACEE("cannot read stringfile '%s'\n", args->string_files[i]); + trace_adal_free_strings(strings); + return 0; + } + strings = tmp_str; + } + return strings; +} + +/* build output filename in newly allocated memory + * if "file" is given "dirorfile/file" is returned, + * otherwise just "dirorfile" + */ +char * build_output_filename(const char *dirorfile, const char *file) +{ + char *outfile; + size_t namelen; + + namelen = strlen(dirorfile) + 1; + if (file) { + namelen += 1 + strlen(file); + } + if (namelen >= FILENAME_MAX -1) { + if (file == NULL) + file = "[NULL]"; + TRACEE("dir/file name too long: %s/%s\n", dirorfile, file); + return 0; + } + outfile = malloc(namelen); + if (outfile == NULL) { + TRACEE("out of memory for output filename"); + return 0; + } + strcpy(outfile, dirorfile); + if (file) { + strcat(outfile, "/"); + strcat(outfile, file); + } + return outfile; +} + +/* run in process mode as daemon */ +int do_daemon(struct arguments *args, trace_strings_t strings) +{ + char *outfile = NULL; + int flags; + int fdout = 1; + int ret = 0; + trace_timeref_t timeref; + + memset(&timeref, 0, sizeof(timeref)); + + TRACEI("Run fsp-trace as a daemon.\n"); + + if (args->output_dir) { + if (!args->output_is_dir) { + outfile = build_output_filename(args->output_dir, 0); + } else { + outfile = build_output_filename(args->output_dir, + args->binary ? "/tracBINARY" : "/tracMERG"); + } + if (outfile == NULL) { + TRACEE("cannot get output filename\n"); + return -EINVAL; + } + fdout = open(outfile, O_RDWR | O_CREAT | O_APPEND, 0666); + if (fdout < 0) { + /* no file no trace! */ + TRACEPE("Error opening file %s to write traces to", outfile); + ret = fdout; + goto free_out; + } + if (args->binary) { + /* if file is new (empty) write header, + * if file isn't empty check header */ + struct stat filestat; + const char tracBINARY_ver = 2; + + ret = fstat(fdout, &filestat); + if (ret < 0) { + TRACEE("Stat failed for outfile '%s'.\n", outfile); + goto free_out; + } + if (filestat.st_size > 0) { + if (is_tracBINARY(outfile) < 1) { + TRACEE("Invalid tracBINARY file %s.\n", outfile); + ret = -ENOENT; + goto free_out; + } + lseek(fdout, 0, SEEK_END); + } else { + write(fdout, &tracBINARY_ver, sizeof(tracBINARY_ver)); + } + } + free(outfile); + outfile = 0; + } + /* else fdout = 1 (stdout) */ + + if (args->comp_count) { + uint32_t i; + + trace_adal_setpipe(-1, -1); /* turn all off except ... */ + for (i = 0; i < args->comp_count; i++) { + trace_adal_setpipe_name(args->comps[i], 127); + } + } else { + trace_adal_setpipe(-1, 127); /* turn all on */ + } + + if (args->process > 1) { + /* daemonize, but don't close stdout */ + ret = daemon(0, 1); + if (ret < 0) { + ret = errno; + TRACEPE("daemoizing failed"); + goto free_out; + } + } + + g_process = 1; + + printf("spawned process pid: %d\n",getpid() ); + + flags = TRACE_PREPEND_BUFFERNAME | TRACE_VERBOSE | TRACE_DONTSTOP; + if (args->binary) + flags |= TRACE_BINARY; + if (args->filename) + flags |= TRACE_FILENAME; + while (1) { + ret = trace_adal_print_pipe(-1, fdout, strings, &timeref, flags); + if (ret < 0) + break; + if (!g_process) { + /* we got a signal (Ctrl-C?), call adal once again */ + flags &= ~TRACE_DONTSTOP; + } + } + free_out: + if (fdout > 2) { + close(fdout); + } + free(outfile); + return ret; +} + +/* process a tracBINARY file + * returns < 0 on error + */ +int do_file_binary(struct arguments *args, const char *file, + trace_strings_t strings, int fdout) +{ + int fd, flags; + int ret = 0; + + TRACEI("tracBINARY is given.\n"); + + fd = open(file, O_RDONLY); + if (fd == -1) { + TRACEPE("Cannot open file %s.\n", file); + return -errno; + } + + flags = TRACE_PREPEND_BUFFERNAME | TRACE_VERBOSE; + if (args->filename) + flags |= TRACE_FILENAME; + if (args->tformat) + flags |= TRACE_TIME_STRFTIME | TRACE_TIMEOFDAY; + if (args->date) + flags |= TRACE_TIMEOFDAY | TRACE_SET_TIMEOFDAY; + + ret = trace_adal_print_pipe(fd, fdout, strings, 0, + flags); + + close(fd); + return ret; +} + +int main(int32_t argc, char *argv[]) +{ + int flags = 0; + int ret=0, mainret=0; + int valid_cnt = 0; + int i, j; + int fd; + struct stat file_stat; + struct iovec *vec_ptr; + trace_strings_t strings = 0; + int fdout = 1; /* default output stdout */ + char *outfile; + + struct argp argp = { options, parse_opt, args_doc, doc, 0, 0, 0}; + struct arguments args; + struct sigaction sig; + struct stat filestat; + + trace_buf_list_t *listp; + trace_buf_list_t *ptr; + + sig.sa_handler = sig_handler; + sigemptyset(&sig.sa_mask); + sig.sa_flags = 0; + + if (sigaction(SIGTERM, &sig, 0)) { + TRACEPE("Register for SIGTERM failed"); + exit(1); + } + + if (sigaction(SIGINT, &sig, 0)) { + TRACEPE("Register for SIGINT failed"); + exit(1); + } + + if (sigaction(SIGQUIT, &sig, 0)) { + TRACEPE("Register for SIGQUIT failed"); + exit(1); + } +#if 0 + /* sig handler for SIGSEGV. usually default is ok, but sometimes + * our own handler provided more information. kept here for debug + */ + if (sigaction(SIGSEGV, &sig, 0)) { + TRACEPE("Register for SIGSEGV failed"); + exit(1); + } +#endif + memset(&args, 0, sizeof(args)); + g_verbose_level = TRACE_DEFAULT_VERBOSE_LEVEL; + + /* avoid error message if called w/o args and no stringfile */ +#ifndef __powerpc__ + if (argc == 1) { + int fd = open(DEFAULT_STRINGFILE, O_RDONLY); + if (fd < 0) { + argp_help(&argp, stderr, ARGP_HELP_STD_USAGE, "fsp-trace"); + return EAGAIN; + } + close(fd); + } +#endif + + /* create memory for maximum number of input files. */ + args.input_files = (char **) malloc(argc * sizeof(char *)); + args.string_files = (char **) malloc(argc * sizeof(char *)); + if (!args.input_files || !args.string_files) { + error(EXIT_FAILURE, errno, "create memory for parsing input files"); + } + + memset(args.input_files, 0, argc * sizeof(char *)); + memset(args.string_files, 0, argc * sizeof(char *)); + + /* get arguments/options */ + if (argp_parse(&argp, argc, argv, ARGP_IN_ORDER, 0, &args) != 0) { + error(EXIT_FAILURE, errno, "parse cmdline"); + } + + /* -d: changing device driver debug level */ + if (args.debug_comp != 0) { + TRACEI("Set debug level (%s:%d).\n", args.debug_comp, args.debug); + ret = trace_adal_setdebug_name(args.debug_comp, args.debug); + if (ret < 0) { + trace_desc_t td; + ret = trace_adal_init_buffer(&td, args.debug_comp, 0); + if (ret < 0 && errno != EAGAIN) { + TRACEPE("cannot access buffer '%s' to set debug level", + args.debug_comp); + return -ret; + } + ret = trace_adal_setdebug(td, args.debug); + if (ret < 0) { + TRACEPE("cannot set debug level for buffer '%s'", + args.debug_comp); + } + } + return -ret; + } + + /* -r: reset/empty all trace buffers */ + if (args.reset == 1) { + TRACEI("Reset all trace buffers on system.\n"); + ret = trace_adal_clear_buffs(); + if (ret < 0) { + TRACEPE("cannot reset buffers"); + } + return -ret; + } + + /* -c: Set per-buffer console trace */ + if (args.console_comp != 0) { + TRACEI("Set console level (%s:%d).\n", args.console_comp, args.console); + ret = trace_adal_setconsole_name(args.console_comp, args.console); + if (ret < 0) { + trace_desc_t td; + ret = trace_adal_init_buffer(&td, args.console_comp, 0); + if (ret < 0 && errno != EAGAIN) { + TRACEPE("cannot access buffer '%s' to set console level", + args.console_comp); + return -ret; + } + ret = trace_adal_setconsole(td, args.console); + if (ret < 0) { + TRACEPE("cannot set console level for buffer '%s'", + args.console_comp); + } + } + return -ret; + } + + if ((args.input_dir) && (args.file_count > 0)) { + TRACEE("Error - Do not support both input directory and input files\n"); + exit(1); + } + + /* if not binary mode (-b) read stringfile */ + if (!args.binary) { + strings = read_stringfiles(&args); + if (strings == 0) { + return ENOENT; + } + } else { + TRACEI("Binary mode.\n"); + if (args.output_dir == NULL && isatty(1)) { + TRACEE("not writing out binary data to a terminal\n"); + return EPERM; + } + } + + + /* daemon mode - reading from pipe */ + if (args.process) { + mainret = do_daemon(&args, strings); + goto out; + } + + /* processing from driver (ioctl) or files. + * open output first */ + if (args.output_dir) { + int openflags = O_RDWR | O_CREAT; + if (!args.output_is_dir) { + /* file given by user */ + outfile = build_output_filename(args.output_dir, 0); + /* with append we'd have to check the type of the file, + * therefore we overwrite the file */ + openflags |= O_TRUNC; + } else { + /* read from files/driver. write smartdump or ascii */ + outfile = build_output_filename(args.output_dir, + args.binary ? "/tracARCHIVE" : "/tracMERG"); + openflags |= O_APPEND; + } + fdout = open(outfile, openflags, 0666); + if (fdout < 0) { + /* no file no trace! */ + TRACEPE("Error opening file %s to write traces to", outfile); + mainret = fdout; + goto out; + } + /* when writing binary we need to check type of file */ + if (args.binary) { + ret = is_smartDump(outfile); + if (ret == 0) { + TRACEE("file '%s' is no a fsp-trace buffer file, cannot append\n", + outfile); + mainret = EINVAL; + goto out; + } + } + } else { + /* not output target given, use stdout */ + fdout = 1; + } + + if (args.input_dir) { + /* handle tracBINARY files from given directory */ + ret = do_file_binary(&args, args.input_dir, strings, fdout); + if (ret < 0) { + mainret = -ret; + goto out; + } + } + + + if (args.files_given) { + /* input files can be tracebuffers, smartdumps or tracBINARY */ + struct iovec buf_vec[args.file_count]; + + memset(buf_vec, 0, sizeof(struct iovec) * args.file_count); + valid_cnt = 0; + for (i = 0; i < args.file_count; i++) { + ret = is_tracBINARY(args.input_files[i]); + if (ret == 1) { + ret = do_file_binary(&args, + args.input_files[i], strings, fdout); + if (ret < 0) { + mainret = -ret; + goto out; + } + continue; + } + + /* not tracBINARY */ + ret = is_smartDump(args.input_files[i]); + if (ret < 1) { + TRACEE("%s: format not recognized, skipping\n", + args.input_files[i]); + continue; + } + /* a smartdump or individual buffer. read file */ + fd = open(args.input_files[i], O_RDONLY); + if (fd < 0) { + TRACEPE("cannot open file '%s'", args.input_files[i]); + mainret = errno; + goto out; + } + if (fstat(fd, &file_stat) < 0) { + /* is_smartDump but not stat??? */ + TRACEPE("cannot stat file '%s'", args.input_files[i]); + mainret = errno; + goto out; + } + vec_ptr = &buf_vec[valid_cnt]; + vec_ptr->iov_len = file_stat.st_size; + vec_ptr->iov_base = (char *) malloc(vec_ptr->iov_len); + if (vec_ptr->iov_base == NULL) { + TRACEPE("malloc failed for buffer size %zu", + vec_ptr->iov_len); + mainret = ENOMEM; + goto out; + } + + ret = read(fd, vec_ptr->iov_base, vec_ptr->iov_len); + if (ret < 0) { + TRACEPE("Failed to read file %s", args.input_files[i]); + free(vec_ptr->iov_base); + close(fd); + continue; + } + + close(fd); + valid_cnt++; + } + + flags = TRACE_PREPEND_BUFFERNAME | TRACE_VERBOSE; + if (args.filename) + flags |= TRACE_FILENAME; + if (!args.nosort) + flags |= TRACE_MIX_BUFFERS; + if (args.tformat) + flags |= TRACE_TIME_STRFTIME | TRACE_TIMEOFDAY; + if (args.date) + flags |= TRACE_TIMEOFDAY | TRACE_SET_TIMEOFDAY; + + if (args.file_count) + trace_adal_print_buffers(buf_vec, valid_cnt, fdout, + strings, 0, flags); + + for (i = 0; i < valid_cnt; i++) { + if (buf_vec[i].iov_base) + free(buf_vec[i].iov_base); + } + + if (args.file_count + (!!args.input_dir) < args.files_given) { + /* files given that are not valid trace buffers. + * don't return success, something went wrong */ + TRACEE("failed to parse all files (skipped %d files)\n", + args.files_given - args.file_count); + mainret = EINVAL; + } + + } else if (!args.input_dir) { + /* read from driver through ioctl if no files/dir given*/ + int bufnum = trace_adal_getbufs(0, 0); + struct iovec *buf_vec; + + if (bufnum < 0) { + /* hack: if adal fails and no arg given print usage */ + if (argc == 1) { + argp_help(&argp, stderr, ARGP_HELP_STD_USAGE, "fsp-trace"); + return EAGAIN; + } + fprintf(stderr, "cannot get number of buffers from device driver\n"); + mainret = -bufnum; + goto out; + } + if (bufnum == 0) { + printf("no trace buffers active.\n"); + mainret = 0; + goto out; + } + listp = (trace_buf_list_t *) malloc(bufnum * sizeof(trace_buf_list_t)); + if (listp == NULL) { + TRACEE("out of memory for buffer list\n"); + mainret = ENOMEM; + goto out; + } + ret = trace_adal_getbufs(bufnum, listp); + if (ret < 0 || ret != bufnum) { + TRACEE("cannot get list of buffers from driver\n"); + mainret = ENOENT; + goto out; + } + + buf_vec = (struct iovec *) malloc(sizeof(struct iovec) * bufnum); + if (buf_vec == NULL) { + TRACEE("out of memory for buffer vector"); + mainret = ENOMEM; + goto out; + } + + ptr = listp; // current buffer + valid_cnt = 0; // valid buffers count + + for (i = 0; i < bufnum; i++, ptr++) { + /* should we get this buffer? */ + char *bufpnt; + + if (!is_buffer_in_list(ptr->name, args.comps, args.comp_count)) { + continue; + } + + bufpnt = (char *) malloc(ptr->size); + if (bufpnt == NULL) { + TRACEPE("out of memory for buffer '%s' (size:%zu)", + ptr->name, ptr->size); + free(listp); + mainret = ENOMEM; + goto out; + } + + /* read the buffer from driver */ + ret = trace_adal_read(ptr->name, ptr->size, (void *) bufpnt); + if (ret < 0) { + TRACEE("cannot read buffer '%s'\n", ptr->name); + free(bufpnt); + continue; + } + + /* binary mode: just write to output file */ + if (args.binary) { + write(fdout, bufpnt, ret); + free(bufpnt); + continue; + } + + vec_ptr = &buf_vec[valid_cnt]; + vec_ptr->iov_len = ret; /* use size returned from driver */ + vec_ptr->iov_base = bufpnt; + valid_cnt++; + } + + free(listp); + if (args.binary) { + TRACEI("%d buffers read.\n", valid_cnt); + mainret = 0; + goto out; + } + + if (valid_cnt == 0) { + TRACEE("no buffer was found/read.\n"); + free(buf_vec); + mainret = ENOENT; + goto out; + } + + + + flags = TRACE_PREPEND_BUFFERNAME; + + if (args.tformat) + flags |= TRACE_TIME_STRFTIME | TRACE_TIMEOFDAY; + if (args.date) + flags |= TRACE_TIMEOFDAY | TRACE_SET_TIMEOFDAY; + if (args.filename) + flags |= TRACE_FILENAME; + if (!args.nosort) + flags |= TRACE_MIX_BUFFERS; + if (args.verbose) + flags |= TRACE_VERBOSE; + + ret = trace_adal_print_buffers(buf_vec, valid_cnt, fdout, + strings, 0, flags); + if( ret < 0 ) { + mainret = -ret; + TRACEE("Print buffer error.\n"); + } + + TRACEI("%d traces read.\n", ret); + + for (j = 0; j < valid_cnt; j++) + free(buf_vec[j].iov_base); + free(buf_vec); + } + + out: + // free memory + trace_adal_free_strings(strings); + if (args.input_files) free(args.input_files); + if (args.string_files) free(args.string_files); + + if (fdout > 2) { + close( fdout ); + if( stat( outfile, &filestat ) == 0 && filestat.st_size == 0 ) + { + TRACEI("%s file (fd:%d size:%d)\n", outfile, (int)fdout, (int)filestat.st_size) + //remove( outfile ); + } + else + { + TRACEI("%s file writed\n", outfile) + } + } + return mainret; +} + @@ -0,0 +1,765 @@ +/* +------------------------------------------------------------------------------- +lookup3.c, by Bob Jenkins, May 2006, Public Domain. + +These are functions for producing 32-bit hashes for hash table lookup. +hashword(), hashlittle(), hashlittle2(), hashbig(), mix(), and final() +are externally useful functions. Routines to test the hash are included +if SELF_TEST is defined. You can use this free for any purpose. It's in +the public domain. It has no warranty. + +You probably want to use hashlittle(). hashlittle() and hashbig() +hash byte arrays. hashlittle() is is faster than hashbig() on +little-endian machines. Intel and AMD are little-endian machines. +On second thought, you probably want hashlittle2(), which is identical to +hashlittle() except it returns two 32-bit hashes for the price of one. +You could implement hashbig2() if you wanted but I haven't bothered here. + +If you want to find a hash of, say, exactly 7 integers, do + a = i1; b = i2; c = i3; + mix(a,b,c); + a += i4; b += i5; c += i6; + mix(a,b,c); + a += i7; + final(a,b,c); +then use c as the hash value. If you have a variable length array of +4-byte integers to hash, use hashword(). If you have a byte array (like +a character string), use hashlittle(). If you have several byte arrays, or +a mix of things, see the comments above hashlittle(). + +Why is this so big? I read 12 bytes at a time into 3 4-byte integers, +then mix those integers. This is fast (you can do a lot more thorough +mixing with 12*3 instructions on 3 integers than you can with 3 instructions +on 1 byte), but shoehorning those bytes into integers efficiently is messy. +------------------------------------------------------------------------------- +*/ +#include <stdio.h> /* defines printf for tests */ +#include <time.h> /* defines time_t for timings in the test */ +#include <stdint.h> /* defines uint32_t etc */ +#include <sys/param.h> /* attempt to define endianness */ +#ifdef linux +# include <endian.h> /* attempt to define endianness */ +#endif + +/* + * My best guess at if you are big-endian or little-endian. This may + * need adjustment. + */ +#if (defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && \ + __BYTE_ORDER == __LITTLE_ENDIAN) || \ + (defined(i386) || defined(__i386__) || defined(__i486__) || \ + defined(__i586__) || defined(__i686__) || defined(vax) || defined(MIPSEL)) +# define HASH_LITTLE_ENDIAN 1 +# define HASH_BIG_ENDIAN 0 +#elif (defined(__BYTE_ORDER) && defined(__BIG_ENDIAN) && \ + __BYTE_ORDER == __BIG_ENDIAN) || \ + (defined(sparc) || defined(POWERPC) || defined(mc68000) || defined(sel)) +# define HASH_LITTLE_ENDIAN 0 +# define HASH_BIG_ENDIAN 1 +#else +# define HASH_LITTLE_ENDIAN 0 +# define HASH_BIG_ENDIAN 0 +#endif + +#define hashsize(n) ((uint32_t)1<<(n)) +#define hashmask(n) (hashsize(n)-1) +#define rot(x,k) (((x)<<(k)) | ((x)>>(32-(k)))) + +/* +------------------------------------------------------------------------------- +mix -- mix 3 32-bit values reversibly. + +This is reversible, so any information in (a,b,c) before mix() is +still in (a,b,c) after mix(). + +If four pairs of (a,b,c) inputs are run through mix(), or through +mix() in reverse, there are at least 32 bits of the output that +are sometimes the same for one pair and different for another pair. +This was tested for: +* pairs that differed by one bit, by two bits, in any combination + of top bits of (a,b,c), or in any combination of bottom bits of + (a,b,c). +* "differ" is defined as +, -, ^, or ~^. For + and -, I transformed + the output delta to a Gray code (a^(a>>1)) so a string of 1's (as + is commonly produced by subtraction) look like a single 1-bit + difference. +* the base values were pseudorandom, all zero but one bit set, or + all zero plus a counter that starts at zero. + +Some k values for my "a-=c; a^=rot(c,k); c+=b;" arrangement that +satisfy this are + 4 6 8 16 19 4 + 9 15 3 18 27 15 + 14 9 3 7 17 3 +Well, "9 15 3 18 27 15" didn't quite get 32 bits diffing +for "differ" defined as + with a one-bit base and a two-bit delta. I +used http://burtleburtle.net/bob/hash/avalanche.html to choose +the operations, constants, and arrangements of the variables. + +This does not achieve avalanche. There are input bits of (a,b,c) +that fail to affect some output bits of (a,b,c), especially of a. The +most thoroughly mixed value is c, but it doesn't really even achieve +avalanche in c. + +This allows some parallelism. Read-after-writes are good at doubling +the number of bits affected, so the goal of mixing pulls in the opposite +direction as the goal of parallelism. I did what I could. Rotates +seem to cost as much as shifts on every machine I could lay my hands +on, and rotates are much kinder to the top and bottom bits, so I used +rotates. +------------------------------------------------------------------------------- +*/ +#define mix(a,b,c) \ +{ \ + a -= c; a ^= rot(c, 4); c += b; \ + b -= a; b ^= rot(a, 6); a += c; \ + c -= b; c ^= rot(b, 8); b += a; \ + a -= c; a ^= rot(c,16); c += b; \ + b -= a; b ^= rot(a,19); a += c; \ + c -= b; c ^= rot(b, 4); b += a; \ +} + +/* +------------------------------------------------------------------------------- +final -- final mixing of 3 32-bit values (a,b,c) into c + +Pairs of (a,b,c) values differing in only a few bits will usually +produce values of c that look totally different. This was tested for +* pairs that differed by one bit, by two bits, in any combination + of top bits of (a,b,c), or in any combination of bottom bits of + (a,b,c). +* "differ" is defined as +, -, ^, or ~^. For + and -, I transformed + the output delta to a Gray code (a^(a>>1)) so a string of 1's (as + is commonly produced by subtraction) look like a single 1-bit + difference. +* the base values were pseudorandom, all zero but one bit set, or + all zero plus a counter that starts at zero. + +These constants passed: + 14 11 25 16 4 14 24 + 12 14 25 16 4 14 24 +and these came close: + 4 8 15 26 3 22 24 + 10 8 15 26 3 22 24 + 11 8 15 26 3 22 24 +------------------------------------------------------------------------------- +*/ +#define final(a,b,c) \ +{ \ + c ^= b; c -= rot(b,14); \ + a ^= c; a -= rot(c,11); \ + b ^= a; b -= rot(a,25); \ + c ^= b; c -= rot(b,16); \ + a ^= c; a -= rot(c,4); \ + b ^= a; b -= rot(a,14); \ + c ^= b; c -= rot(b,24); \ +} + +/* +-------------------------------------------------------------------- + This works on all machines. To be useful, it requires + -- that the key be an array of uint32_t's, and + -- that the length be the number of uint32_t's in the key + + The function hashword() is identical to hashlittle() on little-endian + machines, and identical to hashbig() on big-endian machines, + except that the length has to be measured in uint32_ts rather than in + bytes. hashlittle() is more complicated than hashword() only because + hashlittle() has to dance around fitting the key bytes into registers. +-------------------------------------------------------------------- +*/ +uint32_t hashword( +const uint32_t *k, /* the key, an array of uint32_t values */ +size_t length, /* the length of the key, in uint32_ts */ +uint32_t initval) /* the previous hash, or an arbitrary value */ +{ + uint32_t a,b,c; + + /* Set up the internal state */ + a = b = c = 0xdeadbeef + (((uint32_t)length)<<2) + initval; + + /*------------------------------------------------- handle most of the key */ + while (length > 3) + { + a += k[0]; + b += k[1]; + c += k[2]; + mix(a,b,c); + length -= 3; + k += 3; + } + + /*------------------------------------------- handle the last 3 uint32_t's */ + switch(length) /* all the case statements fall through */ + { + case 3 : c+=k[2]; + case 2 : b+=k[1]; + case 1 : a+=k[0]; + final(a,b,c); + case 0: /* case 0: nothing left to add */ + break; + } + /*------------------------------------------------------ report the result */ + return c; +} + + +/* +-------------------------------------------------------------------- +hashword2() -- same as hashword(), but take two seeds and return two +32-bit values. pc and pb must both be nonnull, and *pc and *pb must +both be initialized with seeds. If you pass in (*pb)==0, the output +(*pc) will be the same as the return value from hashword(). +-------------------------------------------------------------------- +*/ +void hashword2 ( +const uint32_t *k, /* the key, an array of uint32_t values */ +size_t length, /* the length of the key, in uint32_ts */ +uint32_t *pc, /* IN: seed OUT: primary hash value */ +uint32_t *pb) /* IN: more seed OUT: secondary hash value */ +{ + uint32_t a,b,c; + + /* Set up the internal state */ + a = b = c = 0xdeadbeef + ((uint32_t)(length<<2)) + *pc; + c += *pb; + + /*------------------------------------------------- handle most of the key */ + while (length > 3) + { + a += k[0]; + b += k[1]; + c += k[2]; + mix(a,b,c); + length -= 3; + k += 3; + } + + /*------------------------------------------- handle the last 3 uint32_t's */ + switch(length) /* all the case statements fall through */ + { + case 3 : c+=k[2]; + case 2 : b+=k[1]; + case 1 : a+=k[0]; + final(a,b,c); + case 0: /* case 0: nothing left to add */ + break; + } + /*------------------------------------------------------ report the result */ + *pc=c; *pb=b; +} + + +/* +------------------------------------------------------------------------------- +hashlittle() -- hash a variable-length key into a 32-bit value + k : the key (the unaligned variable-length array of bytes) + length : the length of the key, counting by bytes + initval : can be any 4-byte value +Returns a 32-bit value. Every bit of the key affects every bit of +the return value. Two keys differing by one or two bits will have +totally different hash values. + +The best hash table sizes are powers of 2. There is no need to do +mod a prime (mod is sooo slow!). If you need less than 32 bits, +use a bitmask. For example, if you need only 10 bits, do + h = (h & hashmask(10)); +In which case, the hash table should have hashsize(10) elements. + +If you are hashing n strings (uint8_t **)k, do it like this: + for (i=0, h=0; i<n; ++i) h = hashlittle( k[i], len[i], h); + +By Bob Jenkins, 2006. bob_jenkins@burtleburtle.net. You may use this +code any way you wish, private, educational, or commercial. It's free. + +Use for hash table lookup, or anything where one collision in 2^^32 is +acceptable. Do NOT use for cryptographic purposes. +------------------------------------------------------------------------------- +*/ + +uint32_t hashlittle( const void *key, size_t length, uint32_t initval) +{ + uint32_t a,b,c; /* internal state */ + union { const void *ptr; size_t i; } u; /* needed for Mac Powerbook G4 */ + + /* Set up the internal state */ + a = b = c = 0xdeadbeef + ((uint32_t)length) + initval; + + u.ptr = key; + if (HASH_LITTLE_ENDIAN && ((u.i & 0x3) == 0)) { + const uint32_t *k = (const uint32_t *)key; /* read 32-bit chunks */ + const uint8_t *k8; + + /*------ all but last block: aligned reads and affect 32 bits of (a,b,c) */ + while (length > 12) + { + a += k[0]; + b += k[1]; + c += k[2]; + mix(a,b,c); + length -= 12; + k += 3; + } + + /*----------------------------- handle the last (probably partial) block */ + /* + * "k[2]&0xffffff" actually reads beyond the end of the string, but + * then masks off the part it's not allowed to read. Because the + * string is aligned, the masked-off tail is in the same word as the + * rest of the string. Every machine with memory protection I've seen + * does it on word boundaries, so is OK with this. But VALGRIND will + * still catch it and complain. The masking trick does make the hash + * noticably faster for short strings (like English words). + */ +#ifndef VALGRIND + + switch(length) + { + case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; + case 11: c+=k[2]&0xffffff; b+=k[1]; a+=k[0]; break; + case 10: c+=k[2]&0xffff; b+=k[1]; a+=k[0]; break; + case 9 : c+=k[2]&0xff; b+=k[1]; a+=k[0]; break; + case 8 : b+=k[1]; a+=k[0]; break; + case 7 : b+=k[1]&0xffffff; a+=k[0]; break; + case 6 : b+=k[1]&0xffff; a+=k[0]; break; + case 5 : b+=k[1]&0xff; a+=k[0]; break; + case 4 : a+=k[0]; break; + case 3 : a+=k[0]&0xffffff; break; + case 2 : a+=k[0]&0xffff; break; + case 1 : a+=k[0]&0xff; break; + case 0 : return c; /* zero length strings require no mixing */ + } + +#else /* make valgrind happy */ + + k8 = (const uint8_t *)k; + switch(length) + { + case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; + case 11: c+=((uint32_t)k8[10])<<16; /* fall through */ + case 10: c+=((uint32_t)k8[9])<<8; /* fall through */ + case 9 : c+=k8[8]; /* fall through */ + case 8 : b+=k[1]; a+=k[0]; break; + case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */ + case 6 : b+=((uint32_t)k8[5])<<8; /* fall through */ + case 5 : b+=k8[4]; /* fall through */ + case 4 : a+=k[0]; break; + case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */ + case 2 : a+=((uint32_t)k8[1])<<8; /* fall through */ + case 1 : a+=k8[0]; break; + case 0 : return c; + } + +#endif /* !valgrind */ + + } else if (HASH_LITTLE_ENDIAN && ((u.i & 0x1) == 0)) { + const uint16_t *k = (const uint16_t *)key; /* read 16-bit chunks */ + const uint8_t *k8; + + /*--------------- all but last block: aligned reads and different mixing */ + while (length > 12) + { + a += k[0] + (((uint32_t)k[1])<<16); + b += k[2] + (((uint32_t)k[3])<<16); + c += k[4] + (((uint32_t)k[5])<<16); + mix(a,b,c); + length -= 12; + k += 6; + } + + /*----------------------------- handle the last (probably partial) block */ + k8 = (const uint8_t *)k; + switch(length) + { + case 12: c+=k[4]+(((uint32_t)k[5])<<16); + b+=k[2]+(((uint32_t)k[3])<<16); + a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 11: c+=((uint32_t)k8[10])<<16; /* fall through */ + case 10: c+=k[4]; + b+=k[2]+(((uint32_t)k[3])<<16); + a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 9 : c+=k8[8]; /* fall through */ + case 8 : b+=k[2]+(((uint32_t)k[3])<<16); + a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */ + case 6 : b+=k[2]; + a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 5 : b+=k8[4]; /* fall through */ + case 4 : a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */ + case 2 : a+=k[0]; + break; + case 1 : a+=k8[0]; + break; + case 0 : return c; /* zero length requires no mixing */ + } + + } else { /* need to read the key one byte at a time */ + const uint8_t *k = (const uint8_t *)key; + + /*--------------- all but the last block: affect some 32 bits of (a,b,c) */ + while (length > 12) + { + a += k[0]; + a += ((uint32_t)k[1])<<8; + a += ((uint32_t)k[2])<<16; + a += ((uint32_t)k[3])<<24; + b += k[4]; + b += ((uint32_t)k[5])<<8; + b += ((uint32_t)k[6])<<16; + b += ((uint32_t)k[7])<<24; + c += k[8]; + c += ((uint32_t)k[9])<<8; + c += ((uint32_t)k[10])<<16; + c += ((uint32_t)k[11])<<24; + mix(a,b,c); + length -= 12; + k += 12; + } + + /*-------------------------------- last block: affect all 32 bits of (c) */ + switch(length) /* all the case statements fall through */ + { + case 12: c+=((uint32_t)k[11])<<24; + case 11: c+=((uint32_t)k[10])<<16; + case 10: c+=((uint32_t)k[9])<<8; + case 9 : c+=k[8]; + case 8 : b+=((uint32_t)k[7])<<24; + case 7 : b+=((uint32_t)k[6])<<16; + case 6 : b+=((uint32_t)k[5])<<8; + case 5 : b+=k[4]; + case 4 : a+=((uint32_t)k[3])<<24; + case 3 : a+=((uint32_t)k[2])<<16; + case 2 : a+=((uint32_t)k[1])<<8; + case 1 : a+=k[0]; + break; + case 0 : return c; + } + } + + final(a,b,c); + return c; +} + + +/* + * hashlittle2: return 2 32-bit hash values + * + * This is identical to hashlittle(), except it returns two 32-bit hash + * values instead of just one. This is good enough for hash table + * lookup with 2^^64 buckets, or if you want a second hash if you're not + * happy with the first, or if you want a probably-unique 64-bit ID for + * the key. *pc is better mixed than *pb, so use *pc first. If you want + * a 64-bit value do something like "*pc + (((uint64_t)*pb)<<32)". + */ +void hashlittle2( + const void *key, /* the key to hash */ + size_t length, /* length of the key */ + uint32_t *pc, /* IN: primary initval, OUT: primary hash */ + uint32_t *pb) /* IN: secondary initval, OUT: secondary hash */ +{ + uint32_t a,b,c; /* internal state */ + union { const void *ptr; size_t i; } u; /* needed for Mac Powerbook G4 */ + + /* Set up the internal state */ + a = b = c = 0xdeadbeef + ((uint32_t)length) + *pc; + c += *pb; + + u.ptr = key; + if (HASH_LITTLE_ENDIAN && ((u.i & 0x3) == 0)) { + const uint32_t *k = (const uint32_t *)key; /* read 32-bit chunks */ + const uint8_t *k8; + + /*------ all but last block: aligned reads and affect 32 bits of (a,b,c) */ + while (length > 12) + { + a += k[0]; + b += k[1]; + c += k[2]; + mix(a,b,c); + length -= 12; + k += 3; + } + + /*----------------------------- handle the last (probably partial) block */ + /* + * "k[2]&0xffffff" actually reads beyond the end of the string, but + * then masks off the part it's not allowed to read. Because the + * string is aligned, the masked-off tail is in the same word as the + * rest of the string. Every machine with memory protection I've seen + * does it on word boundaries, so is OK with this. But VALGRIND will + * still catch it and complain. The masking trick does make the hash + * noticably faster for short strings (like English words). + */ +#ifndef VALGRIND + + switch(length) + { + case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; + case 11: c+=k[2]&0xffffff; b+=k[1]; a+=k[0]; break; + case 10: c+=k[2]&0xffff; b+=k[1]; a+=k[0]; break; + case 9 : c+=k[2]&0xff; b+=k[1]; a+=k[0]; break; + case 8 : b+=k[1]; a+=k[0]; break; + case 7 : b+=k[1]&0xffffff; a+=k[0]; break; + case 6 : b+=k[1]&0xffff; a+=k[0]; break; + case 5 : b+=k[1]&0xff; a+=k[0]; break; + case 4 : a+=k[0]; break; + case 3 : a+=k[0]&0xffffff; break; + case 2 : a+=k[0]&0xffff; break; + case 1 : a+=k[0]&0xff; break; + case 0 : *pc=c; *pb=b; return; /* zero length strings require no mixing */ + } + +#else /* make valgrind happy */ + + k8 = (const uint8_t *)k; + switch(length) + { + case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; + case 11: c+=((uint32_t)k8[10])<<16; /* fall through */ + case 10: c+=((uint32_t)k8[9])<<8; /* fall through */ + case 9 : c+=k8[8]; /* fall through */ + case 8 : b+=k[1]; a+=k[0]; break; + case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */ + case 6 : b+=((uint32_t)k8[5])<<8; /* fall through */ + case 5 : b+=k8[4]; /* fall through */ + case 4 : a+=k[0]; break; + case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */ + case 2 : a+=((uint32_t)k8[1])<<8; /* fall through */ + case 1 : a+=k8[0]; break; + case 0 : *pc=c; *pb=b; return; /* zero length strings require no mixing */ + } + +#endif /* !valgrind */ + + } else if (HASH_LITTLE_ENDIAN && ((u.i & 0x1) == 0)) { + const uint16_t *k = (const uint16_t *)key; /* read 16-bit chunks */ + const uint8_t *k8; + + /*--------------- all but last block: aligned reads and different mixing */ + while (length > 12) + { + a += k[0] + (((uint32_t)k[1])<<16); + b += k[2] + (((uint32_t)k[3])<<16); + c += k[4] + (((uint32_t)k[5])<<16); + mix(a,b,c); + length -= 12; + k += 6; + } + + /*----------------------------- handle the last (probably partial) block */ + k8 = (const uint8_t *)k; + switch(length) + { + case 12: c+=k[4]+(((uint32_t)k[5])<<16); + b+=k[2]+(((uint32_t)k[3])<<16); + a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 11: c+=((uint32_t)k8[10])<<16; /* fall through */ + case 10: c+=k[4]; + b+=k[2]+(((uint32_t)k[3])<<16); + a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 9 : c+=k8[8]; /* fall through */ + case 8 : b+=k[2]+(((uint32_t)k[3])<<16); + a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */ + case 6 : b+=k[2]; + a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 5 : b+=k8[4]; /* fall through */ + case 4 : a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */ + case 2 : a+=k[0]; + break; + case 1 : a+=k8[0]; + break; + case 0 : *pc=c; *pb=b; return; /* zero length strings require no mixing */ + } + + } else { /* need to read the key one byte at a time */ + const uint8_t *k = (const uint8_t *)key; + + /*--------------- all but the last block: affect some 32 bits of (a,b,c) */ + while (length > 12) + { + a += k[0]; + a += ((uint32_t)k[1])<<8; + a += ((uint32_t)k[2])<<16; + a += ((uint32_t)k[3])<<24; + b += k[4]; + b += ((uint32_t)k[5])<<8; + b += ((uint32_t)k[6])<<16; + b += ((uint32_t)k[7])<<24; + c += k[8]; + c += ((uint32_t)k[9])<<8; + c += ((uint32_t)k[10])<<16; + c += ((uint32_t)k[11])<<24; + mix(a,b,c); + length -= 12; + k += 12; + } + + /*-------------------------------- last block: affect all 32 bits of (c) */ + switch(length) /* all the case statements fall through */ + { + case 12: c+=((uint32_t)k[11])<<24; + case 11: c+=((uint32_t)k[10])<<16; + case 10: c+=((uint32_t)k[9])<<8; + case 9 : c+=k[8]; + case 8 : b+=((uint32_t)k[7])<<24; + case 7 : b+=((uint32_t)k[6])<<16; + case 6 : b+=((uint32_t)k[5])<<8; + case 5 : b+=k[4]; + case 4 : a+=((uint32_t)k[3])<<24; + case 3 : a+=((uint32_t)k[2])<<16; + case 2 : a+=((uint32_t)k[1])<<8; + case 1 : a+=k[0]; + break; + case 0 : *pc=c; *pb=b; return; /* zero length strings require no mixing */ + } + } + + final(a,b,c); + *pc=c; *pb=b; +} + + + +/* + * hashbig(): + * This is the same as hashword() on big-endian machines. It is different + * from hashlittle() on all machines. hashbig() takes advantage of + * big-endian byte ordering. + */ +uint32_t hashbig( const void *key, size_t length, uint32_t initval) +{ + uint32_t a,b,c; + union { const void *ptr; size_t i; } u; /* to cast key to (size_t) happily */ + + /* Set up the internal state */ + a = b = c = 0xdeadbeef + ((uint32_t)length) + initval; + + u.ptr = key; + if (HASH_BIG_ENDIAN && ((u.i & 0x3) == 0)) { + const uint32_t *k = (const uint32_t *)key; /* read 32-bit chunks */ + const uint8_t *k8; + + /*------ all but last block: aligned reads and affect 32 bits of (a,b,c) */ + while (length > 12) + { + a += k[0]; + b += k[1]; + c += k[2]; + mix(a,b,c); + length -= 12; + k += 3; + } + + /*----------------------------- handle the last (probably partial) block */ + /* + * "k[2]<<8" actually reads beyond the end of the string, but + * then shifts out the part it's not allowed to read. Because the + * string is aligned, the illegal read is in the same word as the + * rest of the string. Every machine with memory protection I've seen + * does it on word boundaries, so is OK with this. But VALGRIND will + * still catch it and complain. The masking trick does make the hash + * noticably faster for short strings (like English words). + */ +#ifndef VALGRIND + + switch(length) + { + case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; + case 11: c+=k[2]&0xffffff00; b+=k[1]; a+=k[0]; break; + case 10: c+=k[2]&0xffff0000; b+=k[1]; a+=k[0]; break; + case 9 : c+=k[2]&0xff000000; b+=k[1]; a+=k[0]; break; + case 8 : b+=k[1]; a+=k[0]; break; + case 7 : b+=k[1]&0xffffff00; a+=k[0]; break; + case 6 : b+=k[1]&0xffff0000; a+=k[0]; break; + case 5 : b+=k[1]&0xff000000; a+=k[0]; break; + case 4 : a+=k[0]; break; + case 3 : a+=k[0]&0xffffff00; break; + case 2 : a+=k[0]&0xffff0000; break; + case 1 : a+=k[0]&0xff000000; break; + case 0 : return c; /* zero length strings require no mixing */ + } + +#else /* make valgrind happy */ + + k8 = (const uint8_t *)k; + switch(length) /* all the case statements fall through */ + { + case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; + case 11: c+=((uint32_t)k8[10])<<8; /* fall through */ + case 10: c+=((uint32_t)k8[9])<<16; /* fall through */ + case 9 : c+=((uint32_t)k8[8])<<24; /* fall through */ + case 8 : b+=k[1]; a+=k[0]; break; + case 7 : b+=((uint32_t)k8[6])<<8; /* fall through */ + case 6 : b+=((uint32_t)k8[5])<<16; /* fall through */ + case 5 : b+=((uint32_t)k8[4])<<24; /* fall through */ + case 4 : a+=k[0]; break; + case 3 : a+=((uint32_t)k8[2])<<8; /* fall through */ + case 2 : a+=((uint32_t)k8[1])<<16; /* fall through */ + case 1 : a+=((uint32_t)k8[0])<<24; break; + case 0 : return c; + } + +#endif /* !VALGRIND */ + + } else { /* need to read the key one byte at a time */ + const uint8_t *k = (const uint8_t *)key; + + /*--------------- all but the last block: affect some 32 bits of (a,b,c) */ + while (length > 12) + { + a += ((uint32_t)k[0])<<24; + a += ((uint32_t)k[1])<<16; + a += ((uint32_t)k[2])<<8; + a += ((uint32_t)k[3]); + b += ((uint32_t)k[4])<<24; + b += ((uint32_t)k[5])<<16; + b += ((uint32_t)k[6])<<8; + b += ((uint32_t)k[7]); + c += ((uint32_t)k[8])<<24; + c += ((uint32_t)k[9])<<16; + c += ((uint32_t)k[10])<<8; + c += ((uint32_t)k[11]); + mix(a,b,c); + length -= 12; + k += 12; + } + + /*-------------------------------- last block: affect all 32 bits of (c) */ + switch(length) /* all the case statements fall through */ + { + case 12: c+=k[11]; + case 11: c+=((uint32_t)k[10])<<8; + case 10: c+=((uint32_t)k[9])<<16; + case 9 : c+=((uint32_t)k[8])<<24; + case 8 : b+=k[7]; + case 7 : b+=((uint32_t)k[6])<<8; + case 6 : b+=((uint32_t)k[5])<<16; + case 5 : b+=((uint32_t)k[4])<<24; + case 4 : a+=k[3]; + case 3 : a+=((uint32_t)k[2])<<8; + case 2 : a+=((uint32_t)k[1])<<16; + case 1 : a+=((uint32_t)k[0])<<24; + break; + case 0 : return c; + } + } + + final(a,b,c); + return c; +} + + + diff --git a/trace_adal.c b/trace_adal.c new file mode 100644 index 0000000..4f11799 --- /dev/null +++ b/trace_adal.c @@ -0,0 +1,970 @@ +/* */ +/* OpenPOWER fsp-trace Project */ +/* Contributors Listed Below - COPYRIGHT 2004, 2012. */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ + +/*! + * \file trace_adal.c + * \brief Contains all code to interface with FSP trace device driver. + * + * Please see doxygen output from corresponding .h file for more information + * on functions. + * + * Error handling: I print out a message if the error is found in the ADAL. + * If the error is found in the device driver then the proper + * error will be returned but no error message will be printed + * by ADAL. The device driver will use printk to provide more + * details on these errors. Please see trace_doc.lyx for all + * possible return codes. +*/ + +/* Change Log *****************************************************************/ +/* */ +/* ch# Bugzilla # Userid Date Description */ +/* --- ---------- -------- -------- ---------------------------------------*/ +/* n/a n/a andrewg 09/23/02 Created */ +/* End Change Log *************************************************************/ + +/*----------------------------------------------------------------------------*/ +/* Includes */ +/*----------------------------------------------------------------------------*/ +/* define GUN_SOURCE for pthread_mutexattr_settype() */ +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif +#include <fcntl.h> +#include <sys/ioctl.h> +#include <sys/stat.h> +#include <stdio.h> +#include <errno.h> +#include <unistd.h> +#include <string.h> +#include <ctype.h> +#include "trace_adal.h" +#include <stdarg.h> +#include <stdlib.h> +/*----------------------------------------------------------------------------*/ +/* Constants */ +/*----------------------------------------------------------------------------*/ +#define TRAC_TEMP_BUFFER_SIZE 4096 +/*----------------------------------------------------------------------------*/ + +#ifdef __cplusplus +extern "C" { +#endif + +/*----------------------------------------------------------------------------*/ +/* Globals */ +/*----------------------------------------------------------------------------*/ +static const char copyright [] __attribute__((unused)) + __attribute__((section (".comment"))) = + "Licensed under the Apache License, Version 2.0.\n"; + + +static int fd = -1; +/*----------------------------------------------------------------------------*/ +/* Code */ +/*----------------------------------------------------------------------------*/ + +static int32_t trace_adal_init_fd(void) +{ + /* small window if multi-threaded: might open device twice. + * doesn't really hurts */ + if(fd < 0) { + /* Open the device driver */ + fd = TEMP_FAILURE_RETRY(open(TRACE_FULL_DEVICE_NAME,O_RDWR)); + if(fd < 0) + { + return(TRACE_INIT_FD_ERR); + } + } + return(0); + +} + +static inline void toupper_string(char *text) +{ + unsigned int j; + for(j=0;j < strlen(text);j++) + { + text[j] = toupper(text[j]); + } +} + +static int32_t trac_check_size(int32_t i_size, uint16_t i_cur_length) +{ + int32_t ret = 0; + + if((uint32_t)(i_cur_length + i_size) <= + ((uint32_t)TRAC_TEMP_BUFFER_SIZE - sizeof(trace_entry_head_t))) + { + ret = 0; + } + else + { + //printf("We hit an error!\n"); + ret = -1; + } + return(ret); +} + +/** + * trace_adal_init_buffer - create a tracebuffer and/or get a descriptor for it + * @o_td: the descriptor for the buffer will be written to *o_td + * @i_comp: the name of the buffer that should be created/looked up + * @i_size: the size of the buffer, ignored if buffer exists + */ +int32_t trace_adal_init_buffer(trace_desc_t *o_td,const char *i_comp, + const size_t i_size) +{ + /*--------------------------------------------------------------------*/ + /* Local Variables */ + /*--------------------------------------------------------------------*/ + int32_t ret = 0; + int32_t ret2 = 0; + char name[TRACE_MAX_COMP_NAME_SIZE]; /* must point to 16 char byte name */ + trace_set_buffer_t set_buffer; + + /*--------------------------------------------------------------------*/ + /* Code */ + /*--------------------------------------------------------------------*/ + + *o_td = -1; /* default to invalid */ + + if(fd<0) { /* open trace device if not yet done */ + ret = trace_adal_init_fd(); + if (ret) return(ret); + } + + memset(name,0,TRACE_MAX_COMP_NAME_SIZE); + + if(strlen(i_comp) > (TRACE_MAX_COMP_NAME_SIZE - 1)) + { + ret = TRACE_INIT_BUFF_NAME_ERR; + strcpy(name,"BADN"); + } + else + { + strcpy(name,i_comp); + } + toupper_string(name); + set_buffer.comp = name; + set_buffer.size = i_size; + set_buffer.td = o_td; + ret2 = ioctl( fd, TRACE_SET_BUFFER,&set_buffer); + + if(ret2 < 0) + { + /* report first error if there was one */ + if(ret == 0) + { + ret = TRACE_INIT_BUFF_IOCTL_ERR; + } + } + + return(ret); +} + +/** + * trace_adal_setdebug - set debug level for a tracebuffer + * @i_td: descriptor of a trace buffer to set the debug level + * @i_enable_debug: one of TRACE_DEBUG_ON or TRACE_DEBUG_OFF to enable or disable debug traces + */ +int32_t trace_adal_setdebug(const trace_desc_t i_td, + const int32_t i_enable_debug) +{ + /*--------------------------------------------------------------------*/ + /* Local Variables */ + /*--------------------------------------------------------------------*/ + int32_t ret = 0; + + + /*--------------------------------------------------------------------*/ + /* Code */ + /*--------------------------------------------------------------------*/ + + if(fd<0) { /* open trace device if not yet done */ + ret = trace_adal_init_fd(); + if (ret) return(ret); + } + /* we will check validity of trace descriptor in device driver */ + + if((i_enable_debug == TRACE_DEBUG_OFF) || + (i_enable_debug == TRACE_DEBUG_ON)) + { + ret = ioctl(fd, TRACE_SET_DEBUG,&i_td,&i_enable_debug); + if(ret < 0) + { + ret = TRACE_SETDEBUG_IOCTL_ERR; + } + } + else + { + ret = TRACE_SETDEBUG_INV_PARM_ERR; + } + + return(ret); + +} + +/** + * trace_adal_write - write a trace that consists of one data block + * @i_td: a trace descirptor for the buffer where the trace should be written too + * @i_esize: the size of the trace entry + * @i_debug: whether this is a field or debug trace + * @i_entry: pointer to the trace data + */ +int32_t trace_adal_write(const trace_desc_t i_td,const size_t i_esize, + const int32_t i_debug,const void *i_entry ) +{ + return trace_adal_write2(i_td, i_debug, i_esize, i_entry, 0, 0); +} + +/** + * trace_adal_write2 - write a trace that consists of two data blocks + * @i_td: a trace descirptor for the buffer where the trace should be written too + * @i_debug: whether this is a field or debug trace + * @i_esize: the size of the first part of the trace entry + * @i_entry: pointer to the first part of the trace data + * @i_datasize: the size of the second part of the trace entry + * @i_data: pointer to the second part of the trace data + */ +int32_t trace_adal_write2(const trace_desc_t i_td, const int32_t i_debug, + const size_t i_esize,const void *i_entry, + const size_t i_datasize,const void *i_data) +{ + /*--------------------------------------------------------------------*/ + /* Local Variables */ + /*--------------------------------------------------------------------*/ + int32_t ret = 0; + trace_do_tracev_t do_tracev; + trace_iovec_t do_traceiovec[2]; + + + /*--------------------------------------------------------------------*/ + /* Code */ + /*--------------------------------------------------------------------*/ + + if(fd<0) { /* fail if trace device not open */ + return(TRACE_WRITE_NOT_INIT); + } + /* we will check validity of trace descriptor in device driver */ + + do_tracev.td = i_td; + /* translate fsp-trace-1 debug constants to fsp-trace-2 debug level */ + do_tracev.level = i_debug; // TRACE_FIELD=0 now! == TRACE_FIELD ? 0 : 1; + do_tracev.size = 2 * sizeof(trace_iovec_t); + do_tracev.iovec = do_traceiovec; // must be ptr to the iovec strct. + + do_traceiovec[0].base = i_entry; + do_traceiovec[0].size = i_esize; + do_traceiovec[0].fromuser = 1; + + do_traceiovec[1].base = i_data; + do_traceiovec[1].size = i_datasize; + do_traceiovec[1].fromuser = 1; + + ret = ioctl(fd,TRACE_DO_TRACEV,&do_tracev); + if(ret < 0) + { + ret = TRACE_WRITE_IOCTL_ERR; + } + + return(ret); +} + +/** + * trace_adal_write_all - write a trace, parsing the format string for data count and types + * @i_td: a trace descirptor for the buffer where the trace should be written too + * @i_hash: The hash/trace-id for this trace + * @i_fmt: the printf format string + * @i_line: source line number of trace + * @i_type: whether this is a field or debug trace + * Description: Writes a trace. Parses the format string for % format specifiers to get + * the number and types of parameters. Supports %d,%u,%p,%x and %s. + */ +int32_t trace_adal_write_all(const trace_desc_t i_td,const trace_hash_val i_hash, + const char *i_fmt, + const uint32_t i_line, const int32_t i_type,...) +{ + /*------------------------------------------------------------------------*/ + /* Local Variables */ + /*------------------------------------------------------------------------*/ + va_list ap; + uint8_t ch; + int32_t tmpint; + uint32_t tmpuint; + uint64_t tmpulong; + int32_t longflag = 0; /* =1 if ulonglong or long double (ll/L) */ + int32_t wideflag = 0; /* =1 if "w" specified */ + int32_t precision=0; /* Format precision */ + int32_t width; /* Format width */ + int32_t l_cast = 0; + uint32_t tmp = 0; + char *tmpcharptr = NULL; + char data_buffer[TRAC_TEMP_BUFFER_SIZE]; + trace_entry_head_t *entry = (trace_entry_head_t *)data_buffer; + int32_t ret = 0; + const char *fmt_start = i_fmt; + + /*--------------------------------------------------------------------*/ + /* Code */ + /*--------------------------------------------------------------------*/ + + if(fd<0) { /* fail if trace device not open */ + return(TRACE_WRITE_NOT_INIT); + } + + if(i_td < 0) + { + return(TRACE_WRITE_ALL_BAD_TD); + } + + memset(entry,0,TRAC_TEMP_BUFFER_SIZE); + + entry->tag = TRACE_COMP_TRACE; + entry->line = i_line; + entry->length = 0; + entry->hash = i_hash; + + va_start(ap,i_type); + for (;;) + { + //printf("in the for loop....%s\n",i_fmt); + switch (ch = *i_fmt++) + { + case 0: + goto out; + break; + case '%': + /* Increment past the % */ + ch = *i_fmt++; + + if((ch == '-') || (ch == '+')) + { + /* ignore left/right allignment */ + ch = *i_fmt++; + } + + /* Handle width for hex */ + for (width=0; ch >= '0' && ch <= '9'; ) + { + ch = *i_fmt++; + } /* End for */ + + /* Skip "." and get precision */ + precision = 0; + if (ch == '.') + { + ch = *i_fmt++; + for (precision=0; ch >= '0' && ch <= '9'; ) + { + ch = *i_fmt++; + } /* End for */ + } + if (ch == '#') + { + // ignore # formatting + ch = *i_fmt++; + } + + /* Check for "l" */ + if (ch == 'l') + { + // all 16 bit values will be cast to 32 bit by the trace + // functions so the 'l' flag is redundant. + // Check for second l (ll => 64bit) + ch = *i_fmt++; + if (ch == 'l') { + // ll -> 64bit + longflag = 1; + ch = *i_fmt++; + } + } + + /* Check for "L"/"j"/"q" (64bit int or long double) */ + if (ch == 'L' || ch == 'j' || ch == 'q') + { + longflag = 1; + ch = *i_fmt++; + } + + /* Check for "w" */ + if (ch == 'w') + { + wideflag = 1; + ch = *i_fmt++; + } + switch (ch) + { + case 'c': + l_cast = va_arg(ap,int32_t); + if(trac_check_size(sizeof(int32_t), + entry->length) == 0) + { + memcpy(&(entry->args[entry->length/4]),&l_cast,4); + entry->length += 4; + } else { + ret = -E2BIG; + goto error; + } + break; + case 's': + tmp = entry->length; + tmpcharptr = va_arg(ap, char *); + if(tmpcharptr == NULL) + { + /* NULL pointer, store "NUL" (one L to fit into 4 bytes) */ + if(trac_check_size(strlen("NUL")+1, + entry->length) == 0) + { + trace_adal_copy_string("NUL",&(entry->args[entry->length/4]), + (&tmp)); + entry->length = tmp; + } else { + /* buffer full, not even 4 bytes fit anymore! */ + ret = -E2BIG; + goto error; + } + break; + } + if(trac_check_size(strlen(tmpcharptr)+1, + entry->length) == 0) + { + trace_adal_copy_string(tmpcharptr,&(entry->args[entry->length/4]), + (&tmp)); + entry->length = tmp; + } else if (trac_check_size(4, entry->length) == 0) { + /* string is too long, store just the terminating zero */ + entry->args[entry->length/4] = 0; + entry->length += 4; + } else { + /* buffer full, not even 4 bytes fit anymore! */ + ret = -E2BIG; + goto error; + } + break; + case 'p': + tmpint = va_arg(ap, int32_t); + if(trac_check_size(sizeof(int32_t), + entry->length) == 0) + { + memcpy(&(entry->args[entry->length/4]),&tmpint,4); + entry->length += 4; + } else { + ret = -E2BIG; + goto error; + } + break; + case 'd': + case 'i': + case 'x': + case 'X': + case 'u': + case 'o': + if (longflag) + { + if(trac_check_size(sizeof(uint64_t), + entry->length) == 0) + { + tmpulong = va_arg(ap, uint64_t); + memcpy(&(entry->args[entry->length/4]),&tmpulong,8); + entry->length += 8; + break; + } + } + else + { + if(trac_check_size(sizeof(uint32_t), + entry->length) == 0) + { + tmpuint = va_arg(ap, uint32_t); + memcpy(&(entry->args[entry->length/4]),&tmpuint,4); + entry->length += 4; + break; + } + } + ret = -E2BIG; + goto error; + case 'f': + case 'F': + case 'e': + case 'E': + case 'g': + case 'G': + case 'a': + case 'A': + /* Ignore floating point */ + if (longflag) + { + double dTmp; + dTmp = va_arg(ap, long double); + } + else + { + float fTmp; + fTmp = va_arg(ap, double); + } + break; + default: {} + } + break; + + } + } + + out: + + /* all data is now in entry */ + + if(i_type == TRACE_FIELD) + { + ret = trace_adal_write(i_td,(sizeof(trace_entry_head_t) + + entry->length),TRACE_FIELD, + (void *)(entry)); + } + else + { + ret = trace_adal_write(i_td,(sizeof(trace_entry_head_t) + + entry->length),TRACE_DEBUG, + (void *)(entry)); + } + goto out2; + + error: + entry->tag = TRACE_FIELDSTRING; + entry->line = i_line; + entry->length = 0; + entry->hash = 0; + entry->length = sprintf((char *) entry->args, ">>TRACE TOO BIG (max %u, at pos %u, hash %u)<<", + TRAC_TEMP_BUFFER_SIZE, i_fmt-fmt_start, i_hash); + trace_adal_write(i_td,(sizeof(trace_entry_head_t) + entry->length), + TRACE_FIELD, (void *)(entry)); + + out2: + return(ret); +} + + +/** + * trace_adal_write_ints - write a trace, data consists of a number of int values (32bit) + * @i_td: a trace descirptor for the buffer where the trace should be written too + * @i_debug: whether this is a field or debug trace + * @line: source line number of trace + * @nargs: number of int values + * @hash: The hash/trace-id for this trace + * @p1: the first int value + * @p2: the second int value + * @p3: the third int value + * @p4: the fourth int value + * @p5: the fifth int value + * @p6: the sixth int value + * @p7: the seventh int value + * @p8: the eight int value + * @p9: the nineth int value + * Description: Writes a trace. Doesn't parse format string. printf args have to + * fit into an int (32bit). Number of int values has to be given. + */ +int32_t trace_adal_write_ints(const trace_desc_t i_td, const int32_t i_debug, + uint32_t line, int nargs, uint32_t hash, uint32_t p1, uint32_t p2, + uint32_t p3, uint32_t p4, uint32_t p5, uint32_t p6, uint32_t p7, + uint32_t p8, uint32_t p9) +{ + return trace_adal_write_ints9(i_td, ((i_debug & 0xff) << 24) | ((nargs & 0xff) << 16) | line, + hash, p1, p2, p3, p4, p5, p6, p7,p8, p9); +} + +int32_t trace_adal_write_ints5(const trace_desc_t i_td, const uint32_t i_dln, + uint32_t hash, uint32_t p1, uint32_t p2, + uint32_t p3, uint32_t p4, uint32_t p5) +{ + /*--------------------------------------------------------------------*/ + /* Local Variables */ + /*--------------------------------------------------------------------*/ + trace_do_tracev_t do_tracev; + trace_iovec_t do_traceiovec; + trace_entry_t entry; + int32_t ret = 0; + union { uint32_t u; + struct { uint8_t tag, nargs; uint16_t line; } s; + } opt; + + /*--------------------------------------------------------------------*/ + /* Code */ + /*--------------------------------------------------------------------*/ + + if(fd<0) { /* fail if trace device not open */ + return(TRACE_WRITE_NOT_INIT); + } + /* we will check validity of trace descriptor in device driver */ + + opt.u = i_dln; + entry.h.tag = opt.s.tag ? TRACE_DEBUGTRACE : TRACE_FIELDTRACE; + entry.h.line = opt.s.line; + entry.h.length = sizeof(uint32_t) * opt.s.nargs; + entry.h.hash = hash; + switch(opt.s.nargs) { + case 5: entry.args[4]= p5; /*fall through*/ + case 4: entry.args[3]= p4; /*fall through*/ + case 3: entry.args[2]= p3; /*fall through*/ + case 2: entry.args[1]= p2; /*fall through*/ + case 1: entry.args[0]= p1; /*fall through*/ + default: ; + } + + do_tracev.td = i_td; + do_tracev.level = opt.s.tag; + do_tracev.size = sizeof(trace_iovec_t); // unless more than one + do_tracev.iovec = &do_traceiovec; // must be ptr to the iovec strct. + + do_traceiovec.base = (void *) &entry; + do_traceiovec.size = sizeof(trace_entry_head_t) + entry.h.length; + do_traceiovec.fromuser = 1; + + /* we will check validity of trace descriptor in device driver */ + ret = ioctl(fd,TRACE_DO_TRACEV,&do_tracev); + if(ret < 0) + { + ret = TRACE_WRITE_IOCTL_ERR; + } + + return(ret); +} + +int32_t trace_adal_write_ints9(const trace_desc_t i_td, const uint32_t i_dln, + uint32_t hash, uint32_t p1, uint32_t p2, + uint32_t p3, uint32_t p4, uint32_t p5, uint32_t p6, uint32_t p7, + uint32_t p8, uint32_t p9) +{ + /*--------------------------------------------------------------------*/ + /* Local Variables */ + /*--------------------------------------------------------------------*/ + trace_do_tracev_t do_tracev; + trace_iovec_t do_traceiovec; + trace_entry_t entry; + int32_t ret = 0; + union { uint32_t u; + struct { uint8_t tag, nargs; uint16_t line; } s; + } opt; + + /*--------------------------------------------------------------------*/ + /* Code */ + /*--------------------------------------------------------------------*/ + + if(fd<0) { /* fail if trace device not open */ + return(TRACE_WRITE_NOT_INIT); + } + /* we will check validity of trace descriptor in device driver */ + + opt.u = i_dln; + entry.h.tag = opt.s.tag ? TRACE_DEBUGTRACE : TRACE_FIELDTRACE; + entry.h.line = opt.s.line; + entry.h.length = sizeof(uint32_t) * opt.s.nargs; + entry.h.hash = hash; + switch(opt.s.nargs) { + case 9: entry.args[8]= p9; /*fall through*/ + case 8: entry.args[7]= p8; /*fall through*/ + case 7: entry.args[6]= p7; /*fall through*/ + case 6: entry.args[5]= p6; /*fall through*/ + case 5: entry.args[4]= p5; /*fall through*/ + case 4: entry.args[3]= p4; /*fall through*/ + case 3: entry.args[2]= p3; /*fall through*/ + case 2: entry.args[1]= p2; /*fall through*/ + case 1: entry.args[0]= p1; /*fall through*/ + default: ; + } + + do_tracev.td = i_td; + do_tracev.level = opt.s.tag; + do_tracev.size = sizeof(trace_iovec_t); // unless more than one + do_tracev.iovec = &do_traceiovec; // must be ptr to the iovec strct. + + do_traceiovec.base = (void *) &entry; + do_traceiovec.size = sizeof(trace_entry_head_t) + entry.h.length; + do_traceiovec.fromuser = 1; + + /* we will check validity of trace descriptor in device driver */ + ret = ioctl(fd,TRACE_DO_TRACEV,&do_tracev); + if(ret < 0) + { + ret = TRACE_WRITE_IOCTL_ERR; + } + + return(ret); +} + +/** + * trace_adal_free - release buffer descriptor + * @io_td: a trace descirptor for a buffer. will be invalidated (set to TRACE_DEFAULT_TD) + * Description: Release a buffer descriptor. Currently only sets the descriptor variable + * to a default value. Might do some bookkeeping in the future. + */ +int32_t trace_adal_free(trace_desc_t *io_td) +{ + /*--------------------------------------------------------------------*/ + /* Local Variables */ + /*--------------------------------------------------------------------*/ + int32_t ret = 0; + + + /*--------------------------------------------------------------------*/ + /* Code */ + /*--------------------------------------------------------------------*/ + + /* just set their trace descriptor to default */ + *io_td = TRACE_DEFAULT_TD; + + return(ret); + +} + +/** + * trace_adal_read - read a trace buffer + * @i_comp: name of a trace buffer + * @i_size: size of output buffer + * @o_buff: memory area to write trace buffer to + * Description: Reads a trace buffer. If the buffer is bigger than i_size bytes + * an error is returned. + */ +int32_t trace_adal_read(const char *i_comp,const size_t i_size, void *o_buff) +{ + /*--------------------------------------------------------------------*/ + /* Local Variables */ + /*--------------------------------------------------------------------*/ + int32_t ret = 0; + trace_read_buffer_t read_buffer; + char name[16]; /* must point to 16 char byte name */ + + /*--------------------------------------------------------------------*/ + /* Code */ + /*--------------------------------------------------------------------*/ + + if(fd<0) { /* open trace device if not yet done */ + ret = trace_adal_init_fd(); + if (ret) return(ret); + } + + strcpy(name,i_comp); + toupper_string(name); + + read_buffer.comp = name; + read_buffer.size = i_size; + read_buffer.data = o_buff; + + ret = ioctl(fd,TRACE_READ_BUFFER,&read_buffer); + if(ret < 0) + { + ret = TRACE_READ_IOCTL_ERR; + } + + return(ret); + +} + +/** + * trace_adal_getbufs - get list of registered trace buffers + * @i_lsize: size of buffer for list + * @o_listp: memory area to write trace buffer list to + * Description: Reads list of trace buffers. Writes a list of trace_buf_list_t + * entries. Returns the number of available trace buffers. If more + * than i_lsize buffers are available only this amount of entries + * are written to o_listp. It is valid to call with i_lsize=0 to + * the number of available buffers. + */ +int32_t trace_adal_getbufs(const size_t i_lsize, + trace_buf_list_t *o_listp) +{ + /*--------------------------------------------------------------------*/ + /* Local Variables */ + /*--------------------------------------------------------------------*/ + int32_t ret = 0; + trace_getbufs_t getbufs; + + /*--------------------------------------------------------------------*/ + /* Code */ + /*--------------------------------------------------------------------*/ + + if(fd<0) { /* open trace device if not yet done */ + ret = trace_adal_init_fd(); + if (ret) return(ret); + } + + getbufs.size = i_lsize; + getbufs.list = o_listp; + + ret = ioctl(fd,TRACE_GET_BUFNAMES,&getbufs); + if(ret < 0) + { + ret = TRACE_GETBUFS_IOCTL_ERR; + } + + return(ret); + +} + +/** + * trace_adal_getbufs - get list of registered trace buffers + * @i_comp: name of a trace buffer + * Description: Deletes a trace buffer. + */ +int32_t trace_adal_delete_buffer(const char *i_comp) +{ + /*--------------------------------------------------------------------*/ + /* Local Variables */ + /*--------------------------------------------------------------------*/ + int32_t ret = 0; + char name[16]; /* must point to 16 char byte name */ + + /*--------------------------------------------------------------------*/ + /* Code */ + /*--------------------------------------------------------------------*/ + + if(fd<0) { /* open trace device if not yet done */ + ret = trace_adal_init_fd(); + if (ret) return(ret); + } + + strcpy(name,i_comp); + toupper_string(name); + ret = ioctl(fd,TRACE_DELETE_BUFFER,name); + if(ret < 0) + { + ret = TRACE_DELETE_IOCTL_ERR; + } + + return(ret); +} + +trace_hash_val trace_adal_hash(const char *i_str,const uint32_t i_key) +{ + /*------------------------------------------------------------------------*/ + /* Local Variables */ + /*------------------------------------------------------------------------*/ + + /*------------------------------------------------------------------------*/ + /* Code */ + /*------------------------------------------------------------------------*/ + + register uint32_t l_length; + register uint32_t l_a,l_b,l_c,l_len; + + l_c = i_key; + + //printf("str = %s, size = %d\n",i_str,strlen(i_str)); + + /* Set up the internal state */ + l_len = strlen(i_str); + l_length = l_len; + + + l_a = l_b = 0x9e3779b9; /* the golden ratio; an arbitrary value */ + //printf ("a = %d, b = %d, c = %d\n",l_a,l_b,l_c); + /*---------------------------------------- handle most of the key */ + while (l_len >= 12) + { + l_a += (i_str[0] +((unsigned int)i_str[1]<<8) + + ((unsigned int)i_str[2]<<16) +((unsigned int)i_str[3]<<24)); + l_b += (i_str[4] +((unsigned int)i_str[5]<<8) + + ((unsigned int)i_str[6]<<16) +((unsigned int)i_str[7]<<24)); + l_c += (i_str[8] +((unsigned int)i_str[9]<<8) + + ((unsigned int)i_str[10]<<16)+((unsigned int)i_str[11]<<24)); + //printf ("a = %d, b = %d, c = %d\n",l_a,l_b,l_c); + TRAC_HASH_MIX(l_a,l_b,l_c); + i_str += 12; l_len -= 12; + } + //printf ("a = %d, b = %d, c = %d\n",l_a,l_b,l_c); + /*------------------------------------- handle the last 11 bytes */ + l_c += l_length; + switch(l_len) /* all the case statements fall through */ + { + case 11: l_c+=((unsigned int)i_str[10]<<24); /*fall through*/ + case 10: l_c+=((unsigned int)i_str[9]<<16); /*fall through*/ + case 9 : l_c+=((unsigned int)i_str[8]<<8); /*fall through*/ + /* the first byte of l_c is reserved for the l_length */ + case 8 : l_b+=((unsigned int)i_str[7]<<24); /*fall through*/ + case 7 : l_b+=((unsigned int)i_str[6]<<16); /*fall through*/ + case 6 : l_b+=((unsigned int)i_str[5]<<8); /*fall through*/ + case 5 : l_b+=i_str[4]; /*fall through*/ + case 4 : l_a+=((unsigned int)i_str[3]<<24); /*fall through*/ + case 3 : l_a+=((unsigned int)i_str[2]<<16); /*fall through*/ + case 2 : l_a+=((unsigned int)i_str[1]<<8); /*fall through*/ + case 1 : l_a+=i_str[0]; /*fall through*/ + /* case 0: nothing left to add */ + } + TRAC_HASH_MIX(l_a,l_b,l_c); + /*-------------------------------------------- report the result */ + //printf("i_str = %s, len = %d, hash = %d\n",i_str,strlen(i_str),l_c); + return ((trace_hash_val)l_c); + +} + +int32_t trace_adal_copy_string(const char *i_str,void *o_loc,uint32_t *o_offset) +{ + /*------------------------------------------------------------------------*/ + /* Local Variables */ + /*------------------------------------------------------------------------*/ + int32_t ret = 0; + int32_t boundary; + int32_t len; + + + /*------------------------------------------------------------------------*/ + /* Code */ + /*------------------------------------------------------------------------*/ + strcpy((char *)o_loc,i_str); + len = strlen(i_str) + 1; + boundary = len % 4; + + if(boundary != 0) + { + *o_offset += len + (4 - boundary); + } + else + { + *o_offset += len; + } + + return(ret); +} + +int32_t trace_adal_clear_buffs(void) +{ + /*------------------------------------------------------------------------*/ + /* Local Variables */ + /*------------------------------------------------------------------------*/ + int32_t ret = 0; + + + /*------------------------------------------------------------------------*/ + /* Code */ + /*------------------------------------------------------------------------*/ + + if(fd<0) { /* open trace device if not yet done */ + ret = trace_adal_init_fd(); + if (ret) return(ret); + } + + ret = ioctl(fd,TRACE_CLEAR_BUFFERS,NULL); + if(ret < 0) + { + ret = TRACE_CLEAR_IOCTL_ERR; + } + return(ret); +} +#ifdef __cplusplus +} +#endif diff --git a/trace_adal.h b/trace_adal.h new file mode 100644 index 0000000..5e62c3d --- /dev/null +++ b/trace_adal.h @@ -0,0 +1,872 @@ +/* */ +/* OpenPOWER fsp-trace Project */ +/* Contributors Listed Below - COPYRIGHT 2004, 2009, 2012. */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ + +/****************************************************************************** + * \file trace_adal.h + * \brief Contains header data for trace component.. + * + * The trace component allows an application to trace its execution into + * circular buffers (like a flight recorder) with low performance and + * memory usage impact. This implementation focuses on the Linux operating + * system running on embedded controllers. + * + * \note Please see the document trace_doc.lyx for full documentation on this + * \note component. + *****************************************************************************/ + + +#ifndef _TRACE_ADAL_H +#define _TRACE_ADAL_H + +#include <sys/types.h> +#include <sys/uio.h> +#include <stdint.h> +#include <stdio.h> +#include <sys/time.h> +#include <time.h> +#include <asm/ioctl.h> + +/*----------------------------------------------------------------------------*/ +/* From trace_dd.h, which can not be included in adals. */ +/* Removed doxoygen tags, because they would be redundant. */ +/*----------------------------------------------------------------------------*/ + +#define TRACE_IOCTL_MAGIC 'h' /*!< random magic number for ioctl calls */ + +#define TRACE_SET_BUFFER _IOWR(TRACE_IOCTL_MAGIC,0,int) +#define TRACE_GET_BUFNAMES _IOWR(TRACE_IOCTL_MAGIC,1,int) +#define TRACE_DO_TRACE _IOWR(TRACE_IOCTL_MAGIC,2,int) +#define TRACE_READ_BUFFER _IOWR(TRACE_IOCTL_MAGIC,3,int) +#define TRACE_SET_DEBUG _IOWR(TRACE_IOCTL_MAGIC,4,int) +#define TRACE_SET_INT_TRACE _IOWR(TRACE_IOCTL_MAGIC,5,int) +#define TRACE_UNSET_INT_TRACE _IOWR(TRACE_IOCTL_MAGIC,6,int) +#define TRACE_DELETE_BUFFER _IOWR(TRACE_IOCTL_MAGIC,7,int) +#define TRACE_CLEAR_BUFFERS _IOWR(TRACE_IOCTL_MAGIC,8,int) +#define TRACE_DO_TRACEV _IOWR(TRACE_IOCTL_MAGIC,9,int) +#define TRACE_SET_CONSOLE _IOWR(TRACE_IOCTL_MAGIC,10,int) +#define TRACE_SET_PIPE _IOWR(TRACE_IOCTL_MAGIC,11,int) +#define TRACE_PIPEENABLE _IOWR(TRACE_IOCTL_MAGIC,12,int) //Where could this be passed in (or are kernel functions unused) +#define TRACE_DFR_BUFFER _IOWR(TRACE_IOCTL_MAGIC,13,int) //Enable this? +#define TRACE_READ_DELTA _IOWR(TRACE_IOCTL_MAGIC,14,int) +#define TRACE_SET_THRESHOLD _IOWR(TRACE_IOCTL_MAGIC,15,int) +#define TRACE_READ_RECENT _IOWR(TRACE_IOCTL_MAGIC,18,int) + +/*!@brief Maximum size of component name + * @note Make sure to also change in include/linux/trac.h - + * TRACER_FSP_TRACE_NAME_SIZE +*/ +#define TRACE_MAX_COMP_NAME_SIZE 16 + +#define TRACE_DEBUG_ON 1 /*!<Set to this when debug trace on */ +#define TRACE_DEBUG_OFF 0 /*!<Set to this when debug trace off */ +#define TRACE_DEBUG 1 /*!<Pass this when trace is debug */ +#define TRACE_FIELD 0 /*!<Pass this when trace is field */ + +#define TRACE_COMP_TRACE 0x434F /*!<Identifies trace as a component trace (printf)*/ +#define TRACE_BINARY_TRACE 0x4249 /*!<Identifies trace as a binary trace */ +#define TRACE_INTERNAL_BLOCKED 0xFF42 /*!<Identifies trace as an dd internal trace */ + +#define TRACE_BUFFER_VERSION 1 /*!<Trace buffer version */ +#define TRACE_BUFFER_BINARY_VERSION 2 /*!<Trace buffer version when collected by fsp-trace from pipe */ + +#define TRACE_DEFAULT_TD 0 /*!<Default trace descriptor */ +#define TRACE_PIPE_BUF_SIZE (4 * 4096) /*!<Size of pipe used by trace daemon */ + +#define TRACE_MAX_PROC_COPY 3000 /*!<Maximum data that can be copied at one time through /proc (3072 to be exact) */ + + +/* + * Parsing and output modifier flags + */ +#define TRACE_MIX_BUFFERS 1 +/*!< When multiple buffers are given the traces of all buffers are sorted by timestamp and printed as one list. + * If this flag is not given the traces are printed separatly for each trace buffers (i.e. grouped by buffer). + */ + +#define TRACE_PREPEND_BUFFERNAME 2 +/*!< Show the name of a trace buffer for each trace. The buffer name will be inserted between timestamp and trace text. + * Only one of TRACE_APPEND_BUFFERNAME and TRACE_PREPEND_BUFFERNAME can be given. + */ + +#define TRACE_APPEND_BUFFERNAME 4 +/*!< Show the name of a trace buffer for each trace. The buffer name will be appended at the end of the line + * (after trace text). Only one of TRACE_APPEND_BUFFERNAME and TRACE_PREPEND_BUFFERNAME can be given. + */ + +#define TRACE_TIMEOFDAY 8 +/*!< When set timestamps are translated to timeofday values (date/time). This needs "timeref" to be given. + * If timeref is not given the timestamps are treated as if the PPC timebase counter was started at epoch time + * (i.e. the printed timestamp will be the time since FSP boot time). + */ + +#define TRACE_SET_TIMEOFDAY 16 +/*!< If a TIMEREF trace is found in a trace buffer and timeref is a valid + * pointer the values from the TIMEREF trace are written to timeref. This flag is independent of TRACE_TIMEOFDAY. + */ + +#define TRACE_FILENAME 32 +/*!< Show the name of the source file that contains the trace statement for each trace. + * (at the end of the line, after buffer name if this is printed too). + */ + +#define TRACE_VERBOSE 64 /*!<some messages are printed to STDERR. */ +#define TRACE_IGNORE_VERSION 128 +#define TRACE_OVERWRITE 256 +#define TRACE_BINARY 512 +#define TRACE_DONTSTOP 1024 +/*!< When this is set trace pipe isn't turned off after pipe read + */ + +#define TRACE_TIME_STRFTIME 2048 +/*!< strftime time format flag + */ + + + +#define TRACE_TID_IRQ (1<<31) +/*!< MSB of tid field is used as trace-in-irq flag + */ + +#define TRACE_TID_TID(tid) ((tid) & ~(TRACE_TID_IRQ)) + +/* + * !@enum Will use this to hold line number of trace. + * + */ + enum trace_line_num { trace_line }; + +/* + * !@struct Structure is put at beginning of all trace buffers + */ +struct trace_buf_head_v1 { + uint8_t ver; /*!< version of this struct (1) */ + uint8_t hdr_len; /*!< size of this struct in bytes */ + uint8_t time_flg; /*!< meaning of timestamp entry field */ + uint8_t endian_flg; /*!< flag for big ('B') or little ('L') endian */ + char comp[TRACE_MAX_COMP_NAME_SIZE]; + uint32_t size; /*!< size of buffer, including this struct */ + uint32_t times_wrap; /*!< how often the buffer wrapped */ + uint32_t next_free; /*!< offset of the byte behind the latest entry */ +}; + +struct trace_buf_head_v2 { + uint8_t ver; /*!< version of this struct (1) */ + uint8_t hdr_len; /*!< size of this struct in bytes */ + uint8_t time_flg; /*!< meaning of timestamp entry field */ + uint8_t endian_flg; /*!< flag for big ('B') or little ('L') endian */ + char comp[TRACE_MAX_COMP_NAME_SIZE]; + uint32_t size; /*!< size of buffer, including this struct */ + uint32_t times_wrap; /*!< how often the buffer wrapped */ + uint32_t next_free; /*!< offset of the byte behind the latest entry */ + uint32_t extracted; + uint32_t te_count; +}; + +/* + * !@struct Type definition for trace_buf_head + */ +typedef struct trace_buf_head_v2 trace_buf_head_t; + + +/*! + * @struct Device driver fills in this structure for each trace entry. + * It will put this data first in the trace buffer. + */ +typedef struct trace_entry_stamp { + uint32_t tbh; /*!< timestamp upper part */ + uint32_t tbl; /*!< timestamp lower part */ + uint32_t tid; /*!< process/thread id */ +} trace_entry_stamp_t; + + +/* + * !@struct Structure is used by adal app. layer to fill in trace info. + */ +typedef struct trace_entry_head { + uint16_t length; /*!< size of trace entry */ + uint16_t tag; /*!< type of entry: xTRACE xDUMP, (un)packed */ + uint32_t hash; /*!< a value for the (format) string */ + uint32_t line; /*!< source file line number of trace call */ + uint32_t args[0]; /*!< trace args or data of binary trace */ +} trace_entry_head_t; + + +/* + * !@struct Structure is used to return current components tracing + */ +typedef struct trace_buf_list { + char name[TRACE_MAX_COMP_NAME_SIZE]; /*!< component name */ + size_t size; /*!< size of component trace buffer */ +} trace_buf_list_t; + + +typedef int trace_desc_t; //Type definition for users trace descriptor data type +typedef int tracDesc_t; //Type definition for older trace descriptor type +typedef unsigned long trace_strings_t; /* type for trace strings */ + + +/* + * !@typedef Will use this to hold hash value. + * + */ +typedef uint32_t trace_hash_val; + + +/* + * !@struct Structure to use when using ioctl call TRACE_SET_BUFFER + */ +typedef struct trace_set_buffer { + char *comp; /*!< component name */ + size_t size; /*!< requested size of trace buffer */ + trace_desc_t *td; /*!< will put new trace descriptor here */ +} trace_set_buffer_t; + + +/* + * !@struct Structure to use when using ioctl call TRACE_DO_TRACE + */ +typedef struct trace_do_trace { + trace_desc_t td; /*!< trace descriptor of component buffer to trace to*/ + size_t size; /*!< size of the entry to trace */ + int debug; /*!< #TRACE_DEBUG if debug, #TRACE_FIELD if field */ + const void *data; /*!< pointer to data to trace */ + size_t size2; /*!< size of additional data block (might be 0) */ + const void *data2; /*!< pointer to additional data block */ +} trace_do_trace_t; + + +/* + * @brief Structure to use when using ioctl call TRACE_DO_TRACEV + */ +typedef struct trace_do_tracev { + trace_desc_t td; /*!< trace descriptor of component buffer to trace to*/ + int level; /*!< debug level of trace */ + size_t size; /*!< size of the entry to trace */ + const struct trace_iovec *iovec; /*!< pointer to data to trace */ +} trace_do_tracev_t; + +/* + * @brief Part of struct trace_do_tracev + */ +typedef struct trace_iovec { + const void *base; /* pointer to the data block */ + size_t size; /* size of the data block */ + int fromuser; /* 1 == data in user space (IOW "base" comes from user space) */ +} trace_iovec_t; + +/* + * @brief Structure to use when using ioctl call TRACE_READ_BUFFER + */ +typedef struct trace_read_buffer { + char *comp; /*!< component name of buffer to read */ + size_t size; /*!< number of bytes to read from buffer */ + void *data; /*!< place to put the data */ +} trace_read_buffer_t; + +/* + * @brief Structure to use when using ioctl call TRACE_GET_BUFNAMES + */ +typedef struct trace_getbufs { + size_t size; /*!< number of buffer names to retrieve */ + trace_buf_list_t *list; /*!< place to put the buffer name and size */ +} trace_getbufs_t; + +/* struct for time */ +struct trace_tbtime { + uint32_t high; + uint32_t low; +}; + +/* struct for timeref trace */ +typedef struct trace_timeref { + struct timeval tod; // time of day - sec/usec + struct trace_tbtime time_stamp; // timebase - high/low + uint32_t frequency; // timebase frequency (MHz) +} trace_timeref_t; + + +/*----------------------------------------------------------------------------*/ +/* Constants */ +/*----------------------------------------------------------------------------*/ +#define TRACE_FULL_DEVICE_NAME "/dev/trace/0" +#define TRACE_PROC_DEVICE_NAME "/proc/fsptrace/device" +#define TRACE_PIPE_NAME "/proc/fsptrace/pipe" + +/* additional trace types */ + +/* only define if not defined by trace_dd.h (make different versions of + * these files compatible). check only for one define instead of all */ +#ifndef TRACE_FIELDTRACE + +/*!< a component trace of type field (non-debug): x4654 = "FT" */ +#define TRACE_FIELDTRACE 0x4654 +/*!< a component trace of type debug: x4454 = "DT" */ +#define TRACE_DEBUGTRACE 0x4454 +/*!< a binary trace of type field (non-debug): x4644 = "FD" */ +#define TRACE_FIELDBIN 0x4644 +/*!< a binary trace of type debug: x4644 = "DD" */ +#define TRACE_DEBUGBIN 0x4444 +/*!< a string trace of type field (non-debug): x4653 = "FS" */ +#define TRACE_FIELDSTRING 0x4653 +/*!< a string trace of type debug: x4453 = "DS" */ +#define TRACE_DEBUGSTRING 0x4453 + +#endif + +/*----------------------------------------------------------------------------*/ +/* Return Codes */ +/*----------------------------------------------------------------------------*/ + +/*! + * \defgroup retCodes Return Codes + * + * Errno will be set for all errors. Here are possible errno values: + * - E2BIG: The given buffer size exceeds the maximum size. + * - ENOMEM: Not enough memory to allocate the buffer. + * - EAGAIN: Trace buffer already exists and size requested is larger then it. + * - ENODEV: No trace device driver is loaded in system. + * - EFAULT: Unable to copy user data into kernel space. + * - EINVAL: Trace data pointer is invalid. + * - ENOENT: No trace buffer exitst for component. + * - EACCESS: Permission denied. + + * @{ + */ + +/* trace_adal_init_fd -1 -> -9 */ +/*!< + * Failed to open device, make sure device driver is in kernel. + */ +#define TRACE_INIT_FD_ERR -1 + + +/* trace_adal_init_buffer -10 -> -19 */ + +/*!< + * IOCTL call failed, errno will have more detail. + */ +#define TRACE_INIT_BUFF_IOCTL_ERR -10 + +/*!< + * Invalid component name. + */ +#define TRACE_INIT_BUFF_NAME_ERR -11 + + +/* !< trace_adal_setdebug/console -20 -> -29 + * IOCTL call failed, errno will have more detail. + */ +#define TRACE_SETDEBUG_IOCTL_ERR -20 +#define TRACE_SETCONSOLE_IOCTL_ERR -20 +#define TRACE_SETPIPE_IOCTL_ERR -20 + +/*!< + * Invalid parameter passed to set debug function. + */ +#define TRACE_SETDEBUG_INV_PARM_ERR -21 +#define TRACE_SETCONSOLE_INV_PARM_ERR -21 +#define TRACE_SETPIPE_INV_PARM_ERR -21 + + +/*!< trace_adal_write -30 -> -39 */ +/* + * IOCTL call failed, errno will have more detail. + */ +#define TRACE_WRITE_IOCTL_ERR -30 +/*!< + * trace not initialized yet + */ +#define TRACE_WRITE_NOT_INIT -31 + + +/* trace_adal_free -40 -> -49 */ + + +/*!<trace_adal_read -50 -> -59 + * IOCTL call failed, errno will have more detail. + */ +#define TRACE_READ_IOCTL_ERR -50 + + +/*!<trace_adal_getbufs -60 -> -69 + * IOCTL call failed, errno will have more detail. + */ +#define TRACE_GETBUFS_IOCTL_ERR -60 + + +/*!<trace_adal_delete_buffer -70 -> -79 + * IOCTL call failed, errno will have more detail. + */ +#define TRACE_DELETE_IOCTL_ERR -70 + + +/* trace_adal_hash -80 -> -89 */ +/* trace_adal_copy_string -90 -> -99 */ + +/*!< trace_adal_clear_buffs -100 -> -109 + * IOCTL call failed, errno will have more detail. + */ +#define TRACE_CLEAR_IOCTL_ERR -100 + +/*!< trace_adal_write_all -110 -> -119 + * IOCTL call failed, errno will have more detail. + */ +#define TRACE_WRITE_ALL_IOCTL_ERR -110 +#define TRACE_THREAD_LOCK_FAIL -111 +#define TRACE_THREAD_UNLOCK_FAIL -112 +#define TRACE_WRITE_ALL_BAD_TD -113 + + +/*!<trace_adal_read_partial -120 -> -129 + * Invalid component name. + * Not used anymore, kept not to break old code. + */ +#define TRACE_READ_PARTIAL_ERR -120 + +/*@}*/ + +/*----------------------------------------------------------------------------*/ +/* Types */ +/*----------------------------------------------------------------------------*/ + +/*!@struct a trace entry with args + * the args can be accessed via h.args[..] too + */ +struct trace_entry { + trace_entry_head_t h; + uint32_t args[9]; +}; +typedef struct trace_entry trace_entry_t; + +/*----------------------------------------------------------------------------*/ +/* Globals */ +/*----------------------------------------------------------------------------*/ +/** + * @brief Used by hash function to mix up hash values +*/ +#define TRAC_HASH_MIX(a,b,c) \ +{ \ + a -= b; a -= c; a ^= (c>>13); \ + b -= c; b -= a; b ^= (a<<8); \ + c -= a; c -= b; c ^= (b>>13); \ + a -= b; a -= c; a ^= (c>>12); \ + b -= c; b -= a; b ^= (a<<16); \ + c -= a; c -= b; c ^= (b>>5); \ + a -= b; a -= c; a ^= (c>>3); \ + b -= c; b -= a; b ^= (a<<10); \ + c -= a; c -= b; c ^= (b>>15); \ +} + +/*----------------------------------------------------------------------------*/ +/* Function Prototypes */ +/*----------------------------------------------------------------------------*/ + +#ifdef __cplusplus +extern "C" { +#endif + +/*! + * @brief Initialize a trace buffer for a component. + * + * @param td Device driver will assign caller a trace descriptor. + * @param comp Pointer to 16 character null terminated string. + * @param size Requested buffer size. + * + * @return 0 for success, negative value for failure. + * @retval #TRACE_INIT_BUFF_IOCTL_ERR device driver refused to create buffer + * @retval #TRACE_INIT_BUFF_NAME_ERR buffer name was too long, a buffer with the + name "BADN" was created instead + * @retval #TRACE_INIT_FD_ERR cannot open trace device (module not loaded?), errno set + */ +int32_t trace_adal_init_buffer(trace_desc_t *,const char *,const size_t); + +/*! + * @brief Set trace debug level + * + * @param td Assigned trace descriptor. + * @param level If 0 only field traces will be active. If > 0 debug traces + * with level <= 'level' will be active. + * + * @return 0 for success, negative value for failure. + * @retval #TRACE_SETDEBUG_IOCTL_ERR error from device driver, errno set + * @retval #TRACE_SETDEBUG_INV_PARM_ERR second parm must be TRACE_DEBUG_ON or TRACE_DEBUG_OFF + * @retval #TRACE_INIT_FD_ERR cannot open trace device (module not loaded?), errno set + */ +int32_t trace_adal_setdebug(const trace_desc_t, const int32_t); + +/*! + * @brief Set trace percentage (floored to the closest multiple of 10) level + * + * @param level [1-99] corresponds to 1%-99%. But note that 70-79 = 70%. + * + * @return 0 for success. Errno set on error + */ +int32_t trace_adal_set_threshold(const int32_t); + +/*! + * @brief Set trace console level + * + * @param td Assigned trace descriptor. + * @param level If -1 no traces will be written to console. If 0 only field + * traces will be written to console. If > 0 debug traces + * with level <= 'level' will be written to console too. + * Only active debug traces will be shown on console + * (cf. trace_adal_setdebug). + * + * @return 0 for success, negative value for failure. + * @retval #TRACE_SETCONSOLE_IOCTL_ERR error from device driver, errno set + * @retval #TRACE_SETCONSOLE_INV_PARM_ERR second parm must be -1, 0 or 1 + * @retval #TRACE_INIT_FD_ERR cannot open trace device (module not loaded?), errno set + */ +int32_t trace_adal_setconsole(const trace_desc_t, const int32_t); + +/*! + * @brief Set trace pipe level + * + * @param td Assigned trace descriptor. + * @param level If -1 no traces will be written to pipe. If 0 only field + * traces will be written to pipe. If > 0 debug traces + * with level <= 'level' will be written to pipe too. + * Only active debug traces will be written to pipe + * (cf. trace_adal_setdebug). + * + * @return 0 for success, negative value for failure. + * @retval #TRACE_SETCONSOLE_IOCTL_ERR error from device driver, errno set + * @retval #TRACE_SETCONSOLE_INV_PARM_ERR second parm must be -1, 0 or 1 + * @retval #TRACE_INIT_FD_ERR cannot open trace device (module not loaded?), errno set + */ +int32_t trace_adal_setpipe(const trace_desc_t, const int32_t); + +/* as above, but with buffer names */ +int32_t trace_adal_setdebug_name(const char *name, const int32_t level); +int32_t trace_adal_setconsole_name(const char *name, const int32_t level); +int32_t trace_adal_setpipe_name(const char *name, const int32_t level); + +/*! + * @brief Write some data to trace buffer. + * + * @param td Assigned trace descriptor. + * @param size Size of data. + * @param debug Is it a debug trace or field. + * @param data Data to write to buffer. + * + * @return 0 for success, negative value for failure. + * @retval #TRACE_WRITE_IOCTL_ERR error from device driver, errno set + * @retval #TRACE_WRITE_NOT_INIT trace device isn't opened, call trace_adal_init_buffer before + * @retval #TRACE_INIT_FD_ERR cannot open trace device (module not loaded?), errno set + */ +int32_t trace_adal_write(const trace_desc_t, const size_t, + const int32_t,const void *); + +/*! + * @brief Write some data to trace buffer. + * + * @param td Assigned trace descriptor. + * @param debug Is it a debug trace or field. + * @param size Size of data. + * @param data Data to write to buffer. + * @param size2 Size of second data block. + * @param data2 Second data block to write to buffer. + * + * @return 0 for success, negative value for failure. + * @retval #TRACE_WRITE_IOCTL_ERR error from device driver, errno set + * @retval #TRACE_INIT_FD_ERR cannot open trace device (module not loaded?), errno set + */ +int32_t trace_adal_write2(const trace_desc_t, const int32_t, + const size_t,const void *,const size_t,const void *); + +/*! + * @brief Write some data to trace buffer. + * + * @param td Assigned trace descriptor. + * @param debug Is it a debug trace or field. + * @param count Number of elements in iov + * @param iov Pointer to trace_iovec list + * + * @return 0 for success, negative value for failure. + * @retval #TRACE_WRITE_IOCTL_ERR error from device driver, errno set + * @retval #TRACE_WRITE_NOT_INIT trace device isn't opened, call trace_adal_init_buffer before + * @retval #TRACE_INIT_FD_ERR cannot open trace device (module not loaded?), errno set + */ +int32_t trace_adal_writev(const trace_desc_t, const int32_t, + const size_t, const struct trace_iovec * iov); + +/*! + * @brief Write some data to trace buffer. + * + * @param td Assigned trace descriptor. + * @param debug Is it a debug trace or field. + * @param line source file line number + * @param nargs number of uint32_t args + * @param hash hash value of printf string + * @param p1 - p9 params to store in the trace + * + * @return 0 for success, negative value for failure. + * @retval #TRACE_WRITE_IOCTL_ERR error from device driver, errno set + * @retval #TRACE_INIT_FD_ERR cannot open trace device (module not loaded?), errno set + */ +int32_t trace_adal_write_ints(const trace_desc_t i_td, const int32_t i_debug, + uint32_t line, int nargs, uint32_t hash, uint32_t p1, uint32_t p2, + uint32_t p3, uint32_t p4, uint32_t p5, uint32_t p6, uint32_t p7, + uint32_t p8, uint32_t p9); + +int32_t trace_adal_write_ints5(const trace_desc_t i_td, const uint32_t i_dln, + uint32_t hash, uint32_t p1, uint32_t p2, + uint32_t p3, uint32_t p4, uint32_t p5); +int32_t trace_adal_write_ints9(const trace_desc_t i_td, const uint32_t i_dln, + uint32_t hash, uint32_t p1, uint32_t p2, + uint32_t p3, uint32_t p4, uint32_t p5, uint32_t p6, uint32_t p7, + uint32_t p8, uint32_t p9); + + +/*! + * @brief Let device driver know that you are done tracing for this comp. + * + * @param td Free this trace descriptor + * + * @return Always return 0 + */ +int32_t trace_adal_free(trace_desc_t *io_td); + +/*! + * @brief Read data from a trace buffer. + * trace_adal_read - reads from beginning of buffer + * trace_adal_read_diff - reads from last extract mark. + * trace_adal_read_recent - read from the end of trace buffer. + * + * Note: extract mark only set with trace_adal_read_diff + * + * @param comp Component names buffer you wish to read.. + * @param size Size of data to read. + * @param data_ptr Place to put the data. + * + * @return 0 for success, negative value for failure. + * @retval #TRACE_READ_IOCTL_ERR error from device driver, errno set + * @retval #TRACE_INIT_FD_ERR cannot open trace device (module not loaded?), errno set + */ +int32_t trace_adal_read(const char *, const size_t, void *); +int32_t trace_adal_read_diff(const char *, const size_t, void *); +int32_t trace_adal_read_recent(const char *, const size_t, void *); + +/*! + * @brief Read current buffers and sizes from device driver. + * + * @param num Number of instances of the first parameter being passed. + * @param data_ptr Will put all component names and sizes into this parameter. + * + * @return 0 for success, negative value for failure. + * @retval #TRACE_GETBUFS_IOCTL_ERR error from device driver, errno set + * @retval #TRACE_INIT_FD_ERR cannot open trace device (module not loaded?), errno set + */ +int32_t trace_adal_getbufs(const size_t, trace_buf_list_t *); + +/*! + * @brief Delete trace buffer associated with parameter value. + * + * @param comp Name of component that is to have memory buffer deleted. + * + * @return 0 for success, negative value for failure. + * @retval #TRACE_DELETE_IOCTL_ERR error from device driver, errno set + * @retval #TRACE_INIT_FD_ERR cannot open trace device (module not loaded?), errno set + */ +int32_t trace_adal_delete_buffer(const char *); + +/*! + * @brief Hash the string value into 4 byte hash.. + * + * @param printf_string Pointer to printf string + * @param seed Seed value for hash function + * + * @return Always return 4 byte hash value + */ +trace_hash_val trace_adal_hash(const char *, const uint32_t); + +/*! + * @brief Copy the entire string into our args entry. + * + * @param comp Name of component that is to have memory buffer deleted. + * @param data_ptr Place to copy the data to. + * @param counter Location counter to increment. + * + * @return Always return 0 + */ +int32_t trace_adal_copy_string(const char *,void *,uint32_t*); + +/*! + * @brief + * + * @return 0 for success, negative value for failure. + * @retval #TRACE_CLEAR_IOCTL_ERR error from device driver, errno set + * @retval #TRACE_INIT_FD_ERR cannot open trace device (module not loaded?), errno set + */ +int32_t trace_adal_clear_buffs(void); + +/*! + * @brief Write trace data (can handle all data types) + * + * @return 0 for success, negative value for failure. + * @retval #TRACE_WRITE_ALL_IOCTL_ERR error from device driver, errno set + * @retval #TRACE_WRITE_NOT_INIT trying to trace without device driver + * @retval #TRACE_THREAD_LOCK_FAIL error locking thread lock + * @retval #TRACE_THREAD_UNLOCK_FAIL error unlocking thread lock + * @retval #TRACE_INIT_FD_ERR cannot open trace device (module not loaded?), errno set + * @retval #TRACE_WRITE_ALL_BAD_TD bad trace descriptor + */ +int32_t trace_adal_write_all(const trace_desc_t i_td,const trace_hash_val i_hash, + const char *i_fmt, + const uint32_t i_line, const int32_t i_type,...) + __attribute__ ((format (printf, 3, 6))); + + +/************* interface for parsing buffers *****************/ + +/*! + * @brief Reads a trace string file and adds the information to "strings". + * + * @param strings Destination. + * @param infile File where the stringfile will be read from. + * @param flags Can be: + * TRACE_IGNORE_VERSION If set the stringfile is not checked for a magic header line. + * If not set and the check fails the file is not read and an error is returned + * TRACE_OVERWRITE If a string read from infile has the same id as a string that is already in + * strings but the string itself is different, the one from infile replaces + * (overwrites) the conflicting one in strings. Without this flag such a conflict + * is treated as error and an error returned. + * TRACE_VERBOSE When this is set some messages are printed to STDERR. The messages eg. tell + * about checking the file header (TRACE_CHECK_VERSION), about string + * conflicts and a summary about how much strings have been read. There is no + * formal definition of these messages. + * + * @return on success a pointer to a trace_strings_t structure will be returned. + * On error 0 will be returned and errno set accordingly. + */ +trace_strings_t trace_adal_read_stringfile(trace_strings_t strings, + const char *infile, int flags); + + +/*! + * @brief Deallocates the memory allocated with trace_adal_read_stringfile() for "strings". + * + * @param strings Has to point to a trace_string_t structure that holds information about trace format strings. + */ + +void trace_adal_free_strings(trace_strings_t strings); + + +/*! + * @brief Parses a trace buffer, splits it into the trace entries and writes the traces formatted to the file "outfile". + * + * @param vec Points to a list of "vecsize" number of struct iovec elements with pointers to trace buffers and their sizes. + * @param outfile File descriptor where the traces should be written to using the "write" system call. + * If outfile is "-1" no output is generated. This can be used to check if a buffer is valid + * and to look for timref values from a TIMEREF trace. + * @param strings Has to point to a trace_string_t structure that holds information about trace format strings. + * This structure has to be created with trace_adal_read_stringfile(). + * @param timeref Has to contain a pointer to a trace_timeref_t structure (cf. Section 1.5.4.2, "Trace with time reference + * and timebase information") if one of the flags TRACE_TIMEOFDAY and TRACE_SET_TIMEOFDAY is set. + * This structure contains a pair of time values and the timestamp frequency. These are used to translate the + * timestamp of the traces into timeofday values. If the timeref is 0 timestamp translation is only possible if a + * trace buffer contains a TIMEREF trace entry. Traces read and formatted prior to reading this trace entry are + * shown with untranslated timestamps. + * @param flags Defines the output. It is the "OR"'ed value of some of the following flags: + * - TRACE_MIX_BUFFERS + * When multiple buffers are given the traces of all buffers are sorted by timestamp and printed as one list. + * If this flag is not given the traces are printed separatly for each trace buffers (i.e. grouped by buffer). + * - TRACE_PREPEND_BUFFERNAME + * Show the name of a trace buffer for each trace. The buffer name will be inserted between timestamp and trace text. + * Only one of TRACE_APPEND_BUFFERNAME and TRACE_PREPEND_BUFFERNAME can be given. + * - TRACE_APPEND_BUFFERNAME + * Show the name of a trace buffer for each trace. The buffer name will be appended at the end of the line + * (after trace text). Only one of TRACE_APPEND_BUFFERNAME and TRACE_PREPEND_BUFFERNAME can be given. + * - TRACE_TIMEOFDAY + * When set timestamps are translated to timeofday values (date/time). This needs "timeref" to be given. + * If timeref is not given the timestamps are treated as if the PPC timebase counter was started at epoch time + * (i.e. the printed timestamp will be the time since FSP boot time). + * - TRACE_SET_TIMEOFDAY If a TIMEREF trace is found in a trace buffer and timeref is a valid + * pointer the values from the TIMEREF trace are written to timeref. This flag is independent of TRACE_TIMEOFDAY. + * - TRACE_SET_STRFTIME Will be used to set time in strftime time format. + * Time format passed by _TIME_FORMAT external variable. + * - TRACE_FILENAME + * Show the name of the source file that contains the trace statement for each trace. + * (at the end of the line, after buffer name if this is printed too). + * - TRACE_VERBOSE When this is set some messages are printed to STDERR. The messages + * eg. tell about the processed trace buffers (version, size ...), number of + * traces in them etc. There is no formal definition of these messages. + * + * + * @return on success the number of traces written is returned. On failure a value <0 is returned. + */ +int trace_adal_print_buffers(const struct iovec *vec, size_t vecsize, + int outfile, const trace_strings_t strings, + trace_timeref_t * timeref, int flags); + +/*! + * @brief Reads traces from the trace device driver's pipe or a file. Writes the traces to the file descriptor outfile + * either binary for formatted. The traces will be read from the pipe or the file in chunks to limit memory consumption. + * If the input for this function is a file the whole file will be read and printed. If the input is the trace + * pipe one chunk of data will be read and printed. If the trace pipe buffer isn't full yet the function will sleep + * to wait for the buffer to fill. If the next chunk of data should be read from the pipe the function has to be called again. + * This way the user of this function can handle eg. keyboard input or open a new file to keep the file size below a limit. + * If fd contains -1 traces are read from the trace pipe. If fd contains a valid file descriptor traces are read from + * this file. The file should have been created with this function with the TRACE_BINARY flag set. + * + * @param outfile Is a file descriptor where the traces should be written to using the "write(2)" system call. If out- + * file is "-1" no output is generated. This can be used to check if a buffer is valid and to look for timref values + * from a TIMEREF trace. + * @param strings Has to point to a trace_string_t structure that holds information about trace format strings. This struc- + * ture can be created and filled with trace_adal_read_stringfile(). strings is ignored if the TRACE_BINARY + * flag is set. + * @param timeref Has to contain a pointer to a trace_timeref_t structure (cf. Section 1.5.4.2, "Trace with time reference + * and timebase information") if one of the flags TRACE_TIMEOFDAY and TRACE_SET_TIMEOFDAY is set. + * This structure contains a pair of time values and the timestamp frequency. These are used to translate the + * timestamp of the traces into timeofday values. If the timeref is 0 timestamp translation is only possible if a + * trace buffer contains a TIMEREF trace entry. Traces read and formatted prior to reading this trace entry are + * shown with untranslated timestamps. + * @param flags Defines the output. It is the "OR"'ed value of some of the following flags: + * - TRACE_TIME_????? Specifies the format of the timestamp value of the traces. See time_flg + * for the possible time flags. At least one of these has to be given for version 2 of the trace pipe. + * For pipe versions 3 and above this flag will not be needed and might even be ignored. + * - TRACE_BINARY The traces read from the pipe are not formatted and written in binary format to the file. + * - TRACE_PREPEND_BUFFERNAME, TRACE_APPEND_BUFFERNAME The trace pipe always can contain traces from different trace buffers, + * trace_adal_print_pipe works always in TRACE_MIX_BUFFERS mode. One of these two flags should be given + * to show the buffer a trace was written to (which will correspond to the component that issued the trace). + * Ignored if TRACE_BINARY is set. + * - TRACE_TIMEOFDAY When set timestamps are translated to timeofday values (date/time). This + * needs timeref to be given. If timeref is not given the timestamps are + * treated as if the PPC timebase counter was started at epoch time. Ignored if TRACE_BINARY is set. + * - TRACE_SET_TIMEOFDAY If a TIMEREF trace is found in a trace buffer and timeref is a valid + * pointer the values from the TIMEREF trace are written to timeref.This flag is independent of TRACE_TIMEOFDAY. + * - TRACE_SET_STRFTIME Will be used to set time in strftime time format. + * Time format passed by _TIME_FORMAT extern variable. + * - TRACE_FILENAME Show the name of the source file that contains the trace statement for + * each trace. Ignored if TRACE_BINARY is set. + * - TRACE_VERBOSE When this is set some messages are printed to STDERR. The messages + * eg. tell about the source for the traces (file/pipe), number of traces read etc. + * There is no formal definition of these messages. + * + * @ return on success the number of traces written is returned. On failure a value <0 is returned. + */ +int trace_adal_print_pipe(int fd, int outfile, trace_strings_t strings, + trace_timeref_t * timeref, int flags); + +#ifdef __cplusplus +} +#endif + +#endif |