summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorIvan Mikhaylov <ivan@de.ibm.com>2016-09-02 14:42:59 +0300
committerIvan Mikhaylov <ivan@de.ibm.com>2016-09-19 14:35:37 +0300
commitc15e2267288e26a04f168a3c01f2b09f50f267bf (patch)
treecc8f5e51a0ccd1b8cadbedacc604e48d8a47c079
downloadfsp-trace-c15e2267288e26a04f168a3c01f2b09f50f267bf.tar.gz
fsp-trace-c15e2267288e26a04f168a3c01f2b09f50f267bf.zip
initial fsp-trace commit
-rw-r--r--LICENSE175
-rw-r--r--Makefile11
-rw-r--r--README.md14
-rw-r--r--adal_common.c90
-rw-r--r--adal_common.h169
-rw-r--r--adal_parse.c2693
-rw-r--r--adal_trace.c870
-rw-r--r--copyright.c23
-rw-r--r--fsp-trace.c1247
-rw-r--r--jhash.h765
-rw-r--r--trace_adal.c970
-rw-r--r--trace_adal.h872
12 files changed, 7899 insertions, 0 deletions
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..67db858
--- /dev/null
+++ b/LICENSE
@@ -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(&gtime));
+ time_flg = 1;
+ free(storage_format);
+
+ }
+
+ else if (flags & TRACE_TIMEOFDAY) {
+ gtime = trace_ent->stamp.tbh;
+ strftime(timestr, sizeof(timestr), DEFAULT_TIME_FORMAT,
+ gmtime(&gtime));
+ 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;
+}
+
diff --git a/jhash.h b/jhash.h
new file mode 100644
index 0000000..bb9324d
--- /dev/null
+++ b/jhash.h
@@ -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
OpenPOWER on IntegriCloud