Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Revision 1:506ad37c6bd7, committed 2016-03-31
- Comitter:
- Yogesh Pande
- Date:
- Thu Mar 31 18:10:53 2016 +0300
- Parent:
- 0:fc16a32762df
- Commit message:
- mbed-trace v1.1.0
Changed in this revision
diff -r fc16a32762df -r 506ad37c6bd7 CONTRIBUTING.md --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/CONTRIBUTING.md Thu Mar 31 18:10:53 2016 +0300 @@ -0,0 +1,9 @@ +# Hello! +We are an open source project of [ARM mbed](www.mbed.com). Contributions via [pull request](https://github.com/armmbed/mbed-client-trace/pulls), and [bug reports](https://github.com/armmbed/mbed-client-trace/issues) are welcome! + +For more details please see our general [CONTRIBUTING.md](https://github.com/ARMmbed/greentea/blob/master/docs/CONTRIBUTING.md) document. + +# Contributor agreement +For your pull request to be accepted, we will need you to agree to our [contributor agreement](http://developer.mbed.org/contributor_agreement/) to give us the necessary rights to use and distribute your contributions. (To click through the agreement create an account on mbed.com and log in.) + +<3
diff -r fc16a32762df -r 506ad37c6bd7 LICENSE --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/LICENSE Thu Mar 31 18:10:53 2016 +0300 @@ -0,0 +1,201 @@ +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. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + 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.
diff -r fc16a32762df -r 506ad37c6bd7 README.md --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/README.md Thu Mar 31 18:10:53 2016 +0300 @@ -0,0 +1,123 @@ +# mbed-trace + +A general purpose tracing abstraction library for mbed devices. + +## Description + +The purpose of the library is to provide a light, simple and general tracing solution for mbed devices. By default, it prints traces to `stdout` (usually, a serial port), but the output can also be redirected to other targets. The library was developed using ANSI C language, but it can be used with C++ as well. Currently, there is no C++ wrapper available, but it can be created easily on top of this library. + +## Philosophy + +* The library needs to be light, fast, simple and abstract. +* Dependencies must be minimal. +* The memory space required by the library is allocated at the initialization only once during the application lifetime. +* No new malloc/free are needed when running the library. +* The trace methods must be as fast as possible. +* After a trace method call, the trace function needs to release the required resources. +* A trace method call produces a single line containing `<level>`, `<group>` and `<message>` +* It must be possible to filter messages on the fly. Compile time filtering is not fully supported yet. + +## Compromises + +* The traces are stored as ASCII arrays in the flash memory (pretty high memory consumption). Therefore, it is not necessary to: + * encode/decode the trace messages on the fly (this may take too much CPU time) or + * have external dev-env dependencies to encode the traces compile time and an external application to decode the traces. +* The group name length is limited to four characters. This makes the lines cleaner and it is enough for most use cases for separating the module names. The group name length may not be suitable for a clean human readable format, but still four characters is enough for unique module names. +* The trace function uses `stdout` as the default output target because it goes directly to serial port when initialized. +* The trace function produces traces like: `[<levl>][grp ]: msg`. This provides an easy way to detect trace prints and separate traces from normal prints (for example with _regex_). +* This approach requires a `sprintf` implementation (`stdio.h`). The memory consumption is pretty high, but it allows an efficient way to format traces. +* The solution is not Thread nor Interrupt safe. (Sorry, but the current implementation does not have mutexes. PRs are more than welcome.) + +## Examples of traces + +``` +[DBG ][abc ]: This is a debug message from module abc<cr><lf> +[ERR ][abc ]: Something goes wrong in module abc<cr><lf> +[WARN][br ]: Oh no, br warning occurs!<cr><lf> +[INFO][br ]: Hi there.<cr><lf> +``` + +## Usage + +### Prerequisites + +* Initialize the serial port so that `stdout` works. You can verify that the serial port works using the `printf()` function. + * if you want to redirect the traces somewhere else, see the [trace API](https://github.com/ARMmbed/mbed-trace/blob/master/mbed-trace/mbed_trace.h#L170). +* to activate traces, configure yotta with flag: `YOTTA_CFG_MBED_TRACE` + * By default trace uses 1024 bytes buffer for trace lines, but it can be configure by yotta with: `YOTTA_CFG_MBED_TRACE_LINE_LENGTH`. Default length: 1024. + * To disable ipv6 convertion, set `YOTTA_CFG_MBED_TRACE_FEA_IPV6 = 0` from yotta configurations. +* Call the trace initialization (`mbed_trace_init`) once before using any other APIs. It allocates the trace buffer and initializes the internal variables. +* Define `TRACE_GROUP` in your source code (not in the header!) to use traces. `TRACE_GROUP` is a 1-4 character long char-array (for example `#define TRACE_GROUP "APPL"`). This will be printed on every trace line. + +### Traces + +When you want to print traces, use the `tr_<level>` macros. The macros behave like `printf()`. For example, `tr_debug("hello %s", "trace")` produces the following trace line: `[DBG ][APPL] hello trace<cr><lf>`. + +Available levels: + +* debug +* warning +* error +* info +* cmdline (special behavior, should not be used) + +Initialization (once in application lifetime): + +```c +int mbed_trace_init(void); +``` + +Set output function, `printf` by default: + +```c +mbed_trace_print_function_set(printf) +``` + +### Helping functions + +The purpose of the helping functions is to provide simple conversions, for example from an array to C string, so that you can print everything to single trace line. + +Available conversion functions: +``` +char *mbed_trace_ipv6(const void *addr_ptr) +char *mbed_trace_ipv6_prefix(const uint8_t *prefix, uint8_t prefix_len) +char *mbed_trace_array(const uint8_t *buf, uint16_t len) +``` + +See more in [mbed_trace.h](https://github.com/ARMmbed/mbed-trace/blob/master/mbed-trace/mbed_trace.h). + + +## Usage example: + +```c++ +#define YOTTA_CFG_MBED_TRACE //this can be defined also in the yotta configuration file config.yml +#include "mbed-trace/mbed_trace.h" +#define TRACE_GROUP "main" + +int main(void){ + mbed_trace_init(); // initialize the trace library + tr_debug("this is debug msg"); //-> "[DBG ][main]: this is a debug msg" + tr_err("this is error msg"); //-> "[ERR ][main]: this is an error msg" + tr_warn("this is warning msg"); //-> "[WARN][main]: this is a warning msg" + tr_info("this is info msg"); //-> "[INFO][main]: this is an info msg" + char arr[] = {30, 31, 32}; + tr_debug("printing array: %s", mbed_trace_array(arr, 3)); //-> "[DBG ][main]: printing array: 01:02:03" + return 0; +} +``` + +## Unit tests + +To run unit tests + +* In Linux: +``` +yotta target x86-linux-native +yotta test mbed_trace_test +``` + +* In Windows: +``` +yotta target x86-windows-native +yotta test mbed_trace_test +```
diff -r fc16a32762df -r 506ad37c6bd7 README.txt --- a/README.txt Thu Mar 31 10:04:31 2016 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1 +0,0 @@ -First commit file \ No newline at end of file
diff -r fc16a32762df -r 506ad37c6bd7 mbed-trace/mbed_trace.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mbed-trace/mbed_trace.h Thu Mar 31 18:10:53 2016 +0300 @@ -0,0 +1,327 @@ +/* + * Copyright (c) 2015 ARM Limited. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + * 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 mbed_trace.h + * Trace interface for MbedOS applications. + * This file provide simple but flexible way to handle software traces. + * Trace library are abstract layer, which use stdout (printf) by default, + * but outputs can be easily redirect to custom function, for example to + * store traces to memory or other interfaces. + * + * usage example: + * \code(main.c:) + * #include "mbed_trace.h" + * #define TRACE_GROUP "main" + * + * int main(void){ + * mbed_trace_init(); // initialize trace library + * tr_debug("this is debug msg"); //print debug message to stdout: "[DBG] + * tr_err("this is error msg"); + * tr_warn("this is warning msg"); + * tr_info("this is info msg"); + * return 0; + * } + * \endcode + * Activate with compiler flag: YOTTA_CFG_MBED_TRACE + * Configure trace line buffer size with compiler flag: YOTTA_CFG_MBED_TRACE_LINE_LENGTH. Default length: 1024. + * + */ +#ifndef MBED_TRACE_H_ +#define MBED_TRACE_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef YOTTA_CFG +#include <stdint.h> +#include <stddef.h> +#include <stdbool.h> +#else +#include "ns_types.h" +#endif + +#ifndef YOTTA_CFG_MBED_TRACE_FEA_IPV6 +#define YOTTA_CFG_MBED_TRACE_FEA_IPV6 1 +#endif + +/** 3 upper bits are trace modes related, + and 5 lower bits are trace level configuration */ + +/** Config mask */ +#define TRACE_MASK_CONFIG 0xE0 +/** Trace level mask */ +#define TRACE_MASK_LEVEL 0x1F + +/** plain trace data instead of "headers" */ +#define TRACE_MODE_PLAIN 0x80 +/** color mode */ +#define TRACE_MODE_COLOR 0x40 +/** Use print CR before trace line */ +#define TRACE_CARRIAGE_RETURN 0x20 + +/** used to activate all trace levels */ +#define TRACE_ACTIVE_LEVEL_ALL 0x1F +/** print all traces same as above */ +#define TRACE_ACTIVE_LEVEL_DEBUG 0x1f +/** print info,warn and error traces */ +#define TRACE_ACTIVE_LEVEL_INFO 0x0f +/** print warn and error traces */ +#define TRACE_ACTIVE_LEVEL_WARN 0x07 +/** print only error trace */ +#define TRACE_ACTIVE_LEVEL_ERROR 0x03 +/** print only cmd line data */ +#define TRACE_ACTIVE_LEVEL_CMD 0x01 +/** trace nothing */ +#define TRACE_ACTIVE_LEVEL_NONE 0x00 + +/** this print is some deep information for debug purpose */ +#define TRACE_LEVEL_DEBUG 0x10 +/** Info print, for general purpose prints */ +#define TRACE_LEVEL_INFO 0x08 +/** warning prints, which shouldn't causes any huge problems */ +#define TRACE_LEVEL_WARN 0x04 +/** Error prints, which causes probably problems, e.g. out of mem. */ +#define TRACE_LEVEL_ERROR 0x02 +/** special level for cmdline. Behaviours like "plain mode" */ +#define TRACE_LEVEL_CMD 0x01 + +//usage macros: +#define tr_info(...) mbed_tracef(TRACE_LEVEL_INFO, TRACE_GROUP, __VA_ARGS__) //!< Print info message +#define tr_debug(...) mbed_tracef(TRACE_LEVEL_DEBUG, TRACE_GROUP, __VA_ARGS__) //!< Print debug message +#define tr_warning(...) mbed_tracef(TRACE_LEVEL_WARN, TRACE_GROUP, __VA_ARGS__) //!< Print warning message +#define tr_warn(...) mbed_tracef(TRACE_LEVEL_WARN, TRACE_GROUP, __VA_ARGS__) //!< Alternative warning message +#define tr_error(...) mbed_tracef(TRACE_LEVEL_ERROR, TRACE_GROUP, __VA_ARGS__) //!< Print Error Message +#define tr_err(...) mbed_tracef(TRACE_LEVEL_ERROR, TRACE_GROUP, __VA_ARGS__) //!< Alternative error message +#define tr_cmdline(...) mbed_tracef(TRACE_LEVEL_CMD, TRACE_GROUP, __VA_ARGS__) //!< Special print for cmdline. See more from TRACE_LEVEL_CMD -level + +/** Possible to skip all traces in compile time */ +#if defined(YOTTA_CFG_MBED_TRACE) || (defined(YOTTA_CFG) && !defined(NDEBUG)) + +/** + * Allow specification of default TRACE_GROUP to be used if not specified by application + */ + +#ifndef TRACE_GROUP +#ifdef YOTTA_CFG_MBED_TRACE_GROUP +#define TRACE_GROUP_STR_HELPER(x) #x +#define TRACE_GROUP_STR(x) TRACE_GROUP_STR_HELPER(x) +#define TRACE_GROUP TRACE_GROUP_STR(YOTTA_CFG_MBED_TRACE_GROUP) +#else +#define TRACE_GROUP "APPL" +#endif +#endif + +#if defined(__GNUC__) || defined(__CC_ARM) +/** + * Initialize trace functionality + * @return 0 when all success, otherwise non zero + */ +int mbed_trace_init( void ); +/** + * Free trace memory + */ +void mbed_trace_free( void ); +/** + * Resize buffers (line / tmp ) sizes + * @param lineLength new maximum length for trace line (0 = do no resize) + * @param tmpLength new maximum length for trace tmp buffer (used for trace_array, etc) (0 = do no resize) + */ +void mbed_trace_buffer_sizes(int lineLength, int tmpLength); +/** + * Set trace configurations + * Possible parameters: + * + * TRACE_MODE_COLOR + * TRACE_MODE_PLAIN (this exclude color mode) + * TRACE_CARRIAGE_RETURN (print CR before trace line) + * + * TRACE_ACTIVE_LEVEL_ALL - to activate all trace levels + * or TRACE_ACTIVE_LEVEL_DEBUG (alternative) + * TRACE_ACTIVE_LEVEL_INFO + * TRACE_ACTIVE_LEVEL_WARN + * TRACE_ACTIVE_LEVEL_ERROR + * TRACE_ACTIVE_LEVEL_CMD + * TRACE_LEVEL_NONE - to deactivate all traces + * + * @param config Byte size Bit-mask. Bits are descripted above. + * usage e.g. + * @code + * mbed_trace_config_set( TRACE_ACTIVE_LEVEL_ALL|TRACE_MODE_COLOR ); + * @endcode + */ +void mbed_trace_config_set(uint8_t config); +/** get trace configurations + * @return trace configuration byte + */ +uint8_t mbed_trace_config_get(void); +/** + * Set trace prefix function + * pref_f -function return string with null terminated + * Can be used for e.g. time string + * e.g. + * char* trace_time(){ return "rtc-time-in-string"; } + * mbed_trace_prefix_function_set( &trace_time ); + */ +void mbed_trace_prefix_function_set( char* (*pref_f)(size_t) ); +/** + * Set trace suffix function + * suffix -function return string with null terminated + * Can be used for e.g. time string + * e.g. + * char* trace_suffix(){ return " END"; } + * mbed_trace_suffix_function_set( &trace_suffix ); + */ +void mbed_trace_suffix_function_set(char* (*suffix_f)(void) ); +/** + * Set trace print function + * By default, trace module print using printf() function, + * but with this you can write own print function, + * for e.g. to other IO device. + */ +void mbed_trace_print_function_set( void (*print_f)(const char*) ); +/** + * Set trace print function for tr_cmdline() + */ +void mbed_trace_cmdprint_function_set( void (*printf)(const char*) ); +/** + * When trace group contains text in filters, + * trace print will be ignored. + * e.g.: + * mbed_trace_exclude_filters_set("mygr"); + * mbed_tracef(TRACE_ACTIVE_LEVEL_DEBUG, "ougr", "This is not printed"); + */ +void mbed_trace_exclude_filters_set(char* filters); +/** get trace exclude filters + */ +const char* mbed_trace_exclude_filters_get(void); +/** + * When trace group contains text in filter, + * trace will be printed. + * e.g.: + * set_trace_include_filters("mygr"); + * mbed_tracef(TRACE_ACTIVE_LEVEL_DEBUG, "mygr", "Hi There"); + * mbed_tracef(TRACE_ACTIVE_LEVEL_DEBUG, "grp2", "This is not printed"); + */ +void mbed_trace_include_filters_set(char* filters); +/** get trace include filters + */ +const char* mbed_trace_include_filters_get(void); +/** + * General trace function + * This should be used every time when user want to print out something important thing + * Usage e.g. + * mbed_tracef( TRACE_LEVEL_INFO, "mygr", "Hello world!"); + * + * @param dlevel debug level + * @param grp trace group + * @param fmt trace format (like printf) + * @param ... variable arguments related to fmt + */ +void mbed_tracef(uint8_t dlevel, const char* grp, const char *fmt, ...) __attribute__ ((__format__(__printf__, 3, 4))); +/** + * Get last trace from buffer + */ +const char* mbed_trace_last(void); +#if YOTTA_CFG_MBED_TRACE_FEA_IPV6 == 1 +/** + * mbed_tracef helping function for convert ipv6 + * table to human readable string. + * usage e.g. + * char ipv6[16] = {...}; // ! array length is 16 bytes ! + * mbed_tracef(TRACE_LEVEL_INFO, "mygr", "ipv6 addr: %s", mbed_trace_ipv6(ipv6)); + * + * @param add_ptr IPv6 Address pointer + * @return temporary buffer where ipv6 is in string format + */ +char* mbed_trace_ipv6(const void *addr_ptr); +/** + * mbed_tracef helping function for print ipv6 prefix + * usage e.g. + * char ipv6[16] = {...}; // ! array length is 16 bytes ! + * mbed_tracef(TRACE_LEVEL_INFO, "mygr", "ipv6 addr: %s", mbed_trace_ipv6_prefix(ipv6, 4)); + * + * @param prefix IPv6 Address pointer + * @param prefix_len prefix length + * @return temporary buffer where ipv6 is in string format + */ +char* mbed_trace_ipv6_prefix(const uint8_t *prefix, uint8_t prefix_len); +#endif +/** + * mbed_tracef helping function for convert hex-array to string. + * usage e.g. + * char myarr[] = {0x10, 0x20}; + * mbed_tracef(TRACE_LEVEL_INFO, "mygr", "arr: %s", mbed_trace_array(myarr, 2)); + * + * @param buf hex array pointer + * @param len buffer length + * @return temporary buffer where string copied + * if array as string not fit to temp buffer, this function write '*' as last character, + * which indicate that buffer is too small for array. + */ +char* mbed_trace_array(const uint8_t* buf, uint16_t len); + +#else // defined(__GNUC__) || defined(__CC_ARM) +int mbed_trace_init( void ); +void mbed_trace_free( void ); +void mbed_trace_config_set(uint8_t config); +void mbed_trace_prefix_function_set( char* (*pref_f)(size_t) ); +void mbed_trace_suffix_function_set(char* (*suffix_f)(void) ); +void mbed_trace_print_function_set( void (*print_f)(const char*) ); +void mbed_trace_cmdprint_function_set( void (*printf)(const char*) ); +void mbed_trace_exclude_filters_set(char* filters); +const char* mbed_trace_exclude_filters_get(void); +void mbed_trace_include_filters_set(char* filters); +const char* mbed_trace_include_filters_get(void); +void mbed_tracef(uint8_t dlevel, const char* grp, const char *fmt, ...); +const char* mbed_trace_last(void); +char* mbed_trace_array(const uint8_t* buf, uint16_t len); +#if YOTTA_CFG_MBED_TRACE_FEA_IPV6 == 1 +char* mbed_trace_ipv6(const void *addr_ptr); +char* mbed_trace_ipv6_prefix(const uint8_t *prefix, uint8_t prefix_len); +#endif // YOTTA_CFG_MBED_TRACE_FEA_IPV6 + +#endif // defined(__GNUC__) || defined(__CC_ARM) + + +#else // YOTTA_CFG_MBED_TRACE + +// trace functionality not supported +#define mbed_trace_init(...) ((void) 0) +#define mbed_trace_free(...) ((void) 0) +#define mbed_trace_config_set(...) ((void) 0) +#define mbed_trace_prefix_function_set(...) ((void) 0) +#define mbed_trace_suffix_function_set(...) ((void) 0) +#define mbed_trace_print_function_set(...) ((void) 0) +#define mbed_trace_cmdprint_function_set(...) ((void) 0) +#define mbed_trace_exclude_filters_get(...) ((void) 0) +#define mbed_trace_include_filters_set(...) ((void) 0) +#define mbed_trace_include_filters_get(...) ((void) 0) + +#define mbed_trace_last(...) ((void) 0) +#define mbed_tracef(...) ((void) 0) +#define mbed_trace_ipv6(...) ((void) 0) +#define mbed_trace_array(...) ((void) 0) +#define mbed_trace_ipv6_prefix(...) ((void) 0) + +#endif //YOTTA_CFG_MBED_TRACE + +#ifdef __cplusplus +} +#endif + +#endif /* MBED_TRACE_H_ */
diff -r fc16a32762df -r 506ad37c6bd7 mbed-trace/mbed_trace_ip6tos.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mbed-trace/mbed_trace_ip6tos.h Thu Mar 31 18:10:53 2016 +0300 @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2014-2015 ARM Limited. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + * 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 MBED_TRACE_IP6TOS_H +#define MBED_TRACE_IP6TOS_H +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Print binary IPv6 address to a string. + * String must contain enough room for full address, 40 bytes exact. + * IPv4 tunneling addresses are not covered. + * \param ip6addr IPv6 address. + * \p buffer to write string to. + */ +void mbed_trace_ip6tos(const void *ip6addr, char *p); + + +#ifdef __cplusplus +} +#endif +#endif
diff -r fc16a32762df -r 506ad37c6bd7 source/CMakeLists.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/source/CMakeLists.txt Thu Mar 31 18:10:53 2016 +0300 @@ -0,0 +1,14 @@ +if(DEFINED TARGET_LIKE_X86_LINUX_NATIVE) + add_library( mbed-trace + mbed_trace.c + mbed_trace_ip6tos.c + ) + add_definitions("-g -O0 -fprofile-arcs -ftest-coverage") + target_link_libraries(mbed-trace gcov) +else() + add_library( mbed-trace + mbed_trace.c + mbed_trace_ip6tos.c + ) + target_link_libraries(mbed-trace) +endif()
diff -r fc16a32762df -r 506ad37c6bd7 source/mbed_trace.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/source/mbed_trace.c Thu Mar 31 18:10:53 2016 +0300 @@ -0,0 +1,517 @@ +/* + * Copyright (c) 2014-2015 ARM Limited. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + * 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 <stdio.h> +#include <string.h> +#include <stdarg.h> +#include <stdlib.h> + +#ifndef YOTTA_CFG_MBED_TRACE +#define YOTTA_CFG_MBED_TRACE +#define YOTTA_CFG_MBED_TRACE_FEA_IPV6 1 +#endif + +#include "mbed-trace/mbed_trace.h" +#if YOTTA_CFG_MTRACE_FEA_IPV6 == 1 +#include "mbed-trace/mbed_trace_ip6tos.h" +#endif + +#ifndef MEM_ALLOC +#define MEM_ALLOC malloc +#endif +#ifndef MEM_FREE +#define MEM_FREE free +#endif + +#define VT100_COLOR_ERROR "\x1b[31m" +#define VT100_COLOR_WARN "\x1b[33m" +#define VT100_COLOR_INFO "\x1b[39m" +#define VT100_COLOR_DEBUG "\x1b[90m" + +/** default max trace line size in bytes */ +#ifdef YOTTA_CFG_MBED_TRACE_LINE_LENGTH +#define DEFAULT_TRACE_LINE_LENGTH YOTTA_CFG_MBED_TRACE_LINE_LENGTH +#else +#define DEFAULT_TRACE_LINE_LENGTH 1024 +#endif +/** default max temporary buffer size in bytes, used in + trace_ipv6, trace_array and trace_strn */ +#ifdef YOTTA_CFG_MTRACE_TMP_LINE_LEN +#define DEFAULT_TRACE_TMP_LINE_LEN YOTTA_CFG_MTRACE_TMP_LINE_LEN +#else +#define DEFAULT_TRACE_TMP_LINE_LEN 128 +#endif +/** default max filters (include/exclude) length in bytes */ +#define DEFAULT_TRACE_FILTER_LENGTH 24 + +/** default print function, just redirect str to printf */ +static void mbed_trace_realloc( char **buffer, int *length_ptr, int new_length); +static void mbed_trace_default_print(const char *str); +static void mbed_trace_reset_tmp(void); + +typedef struct trace_s { + /** trace configuration bits */ + uint8_t trace_config; + /** exclude filters list, related group name */ + char *filters_exclude; + /** include filters list, related group name */ + char *filters_include; + /** Filters length */ + int filters_length; + /** trace line */ + char *line; + /** trace line length */ + int line_length; + /** temporary data */ + char *tmp_data; + /** temporary data array length */ + int tmp_data_length; + /** temporary data pointer */ + char *tmp_data_ptr; + + /** prefix function, which can be used to put time to the trace line */ + char *(*prefix_f)(size_t); + /** suffix function, which can be used to some string to the end of trace line */ + char *(*suffix_f)(void); + /** print out function. Can be redirect to flash for example. */ + void (*printf)(const char *); + /** print out function for TRACE_LEVEL_CMD */ + void (*cmd_printf)(const char *); +} trace_t; + +static trace_t m_trace = { + .filters_exclude = 0, + .filters_include = 0, + .line = 0, + .tmp_data = 0, + .prefix_f = 0, + .suffix_f = 0, + .printf = 0, + .cmd_printf = 0 +}; + +int mbed_trace_init(void) +{ + m_trace.trace_config = TRACE_MODE_COLOR | TRACE_ACTIVE_LEVEL_ALL | TRACE_CARRIAGE_RETURN; + m_trace.line_length = DEFAULT_TRACE_LINE_LENGTH; + if (m_trace.line == NULL) { + m_trace.line = MEM_ALLOC(m_trace.line_length); + } + m_trace.tmp_data_length = DEFAULT_TRACE_TMP_LINE_LEN; + if (m_trace.tmp_data == NULL) { + m_trace.tmp_data = MEM_ALLOC(m_trace.tmp_data_length); + } + m_trace.tmp_data_ptr = m_trace.tmp_data; + m_trace.filters_length = DEFAULT_TRACE_FILTER_LENGTH; + if (m_trace.filters_exclude == NULL) { + m_trace.filters_exclude = MEM_ALLOC(m_trace.filters_length); + } + if (m_trace.filters_include == NULL) { + m_trace.filters_include = MEM_ALLOC(m_trace.filters_length); + } + + if (m_trace.line == NULL || + m_trace.tmp_data == NULL || + m_trace.filters_exclude == NULL || + m_trace.filters_include == NULL) { + //memory allocation fail + mbed_trace_free(); + return -1; + } + memset(m_trace.tmp_data, 0, m_trace.tmp_data_length); + memset(m_trace.filters_exclude, 0, m_trace.filters_length); + memset(m_trace.filters_include, 0, m_trace.filters_length); + memset(m_trace.line, 0, m_trace.line_length); + + m_trace.prefix_f = 0; + m_trace.suffix_f = 0; + m_trace.printf = mbed_trace_default_print; + m_trace.cmd_printf = 0; + + return 0; +} +void mbed_trace_free(void) +{ + MEM_FREE(m_trace.line); + m_trace.line_length = 0; + m_trace.line = 0; + MEM_FREE(m_trace.tmp_data); + m_trace.tmp_data = 0; + m_trace.tmp_data_ptr = 0; + MEM_FREE(m_trace.filters_exclude); + m_trace.filters_exclude = 0; + MEM_FREE(m_trace.filters_include); + m_trace.filters_include = 0; + m_trace.filters_length = 0; + m_trace.prefix_f = 0; + m_trace.suffix_f = 0; + m_trace.printf = mbed_trace_default_print; + m_trace.cmd_printf = 0; +} +static void mbed_trace_realloc( char **buffer, int *length_ptr, int new_length) +{ + MEM_FREE(*buffer); + *buffer = MEM_ALLOC(new_length); + *length_ptr = new_length; +} +void mbed_trace_buffer_sizes(int lineLength, int tmpLength) +{ + if( lineLength > 0 ) { + mbed_trace_realloc( &(m_trace.line), &m_trace.line_length, lineLength ); + } + if( tmpLength > 0 ) { + mbed_trace_realloc( &(m_trace.tmp_data), &m_trace.tmp_data_length, tmpLength); + mbed_trace_reset_tmp(); + } +} +void mbed_trace_config_set(uint8_t config) +{ + m_trace.trace_config = config; +} +uint8_t mbed_trace_config_get(void) +{ + return m_trace.trace_config; +} +void mbed_trace_prefix_function_set(char *(*pref_f)(size_t)) +{ + m_trace.prefix_f = pref_f; +} +void mbed_trace_suffix_function_set(char *(*suffix_f)(void)) +{ + m_trace.suffix_f = suffix_f; +} +void mbed_trace_print_function_set(void (*printf)(const char *)) +{ + m_trace.printf = printf; +} +void mbed_trace_cmdprint_function_set(void (*printf)(const char *)) +{ + m_trace.cmd_printf = printf; +} +void mbed_trace_exclude_filters_set(char *filters) +{ + if (filters) { + (void)strncpy(m_trace.filters_exclude, filters, m_trace.filters_length); + } else { + m_trace.filters_exclude[0] = 0; + } +} +const char *mbed_trace_exclude_filters_get(void) +{ + return m_trace.filters_exclude; +} +const char *mbed_trace_include_filters_get(void) +{ + return m_trace.filters_include; +} +void mbed_trace_include_filters_set(char *filters) +{ + if (filters) { + (void)strncpy(m_trace.filters_include, filters, m_trace.filters_length); + } else { + m_trace.filters_include[0] = 0; + } +} +static int8_t mbed_trace_skip(int8_t dlevel, const char *grp) +{ + if (dlevel >= 0 && grp != 0) { + // filter debug prints only when dlevel is >0 and grp is given + + /// @TODO this could be much better.. + if (m_trace.filters_exclude[0] != '\0' && + strstr(m_trace.filters_exclude, grp) != 0) { + //grp was in exclude list + return 1; + } + if (m_trace.filters_include[0] != '\0' && + strstr(m_trace.filters_include, grp) == 0) { + //grp was in include list + return 1; + } + } + return 0; +} +static void mbed_trace_default_print(const char *str) +{ + puts(str); +} +void mbed_tracef(uint8_t dlevel, const char *grp, const char *fmt, ...) +{ + if (NULL == m_trace.line) { + return; + } + + m_trace.line[0] = 0; //by default trace is empty + + if (mbed_trace_skip(dlevel, grp) || fmt == 0 || grp == 0 || !m_trace.printf) { + //return tmp data pointer back to the beginning + mbed_trace_reset_tmp(); + return; + } + if ((m_trace.trace_config & TRACE_MASK_LEVEL) & dlevel) { + bool color = (m_trace.trace_config & TRACE_MODE_COLOR) != 0; + bool plain = (m_trace.trace_config & TRACE_MODE_PLAIN) != 0; + bool cr = (m_trace.trace_config & TRACE_CARRIAGE_RETURN) != 0; + + int retval = 0, bLeft = m_trace.line_length; + char *ptr = m_trace.line; + if (plain == true || dlevel == TRACE_LEVEL_CMD) { + va_list ap; + va_start(ap, fmt); + //add trace data + retval = vsnprintf(ptr, bLeft, fmt, ap); + va_end(ap); + if (dlevel == TRACE_LEVEL_CMD && m_trace.cmd_printf) { + m_trace.cmd_printf(m_trace.line); + m_trace.cmd_printf("\n"); + } else { + //print out whole data + m_trace.printf(m_trace.line); + } + } else { + if (color) { + if (cr) { + retval = snprintf(ptr, bLeft, "\r\x1b[2K"); + if (retval >= bLeft) { + retval = 0; + } + if (retval > 0) { + ptr += retval; + bLeft -= retval; + } + } + if (bLeft > 0) { + //include color in ANSI/VT100 escape code + switch (dlevel) { + case (TRACE_LEVEL_ERROR): + retval = snprintf(ptr, bLeft, "%s", VT100_COLOR_ERROR); + break; + case (TRACE_LEVEL_WARN): + retval = snprintf(ptr, bLeft, "%s", VT100_COLOR_WARN); + break; + case (TRACE_LEVEL_INFO): + retval = snprintf(ptr, bLeft, "%s", VT100_COLOR_INFO); + break; + case (TRACE_LEVEL_DEBUG): + retval = snprintf(ptr, bLeft, "%s", VT100_COLOR_DEBUG); + break; + default: + color = 0; //avoid unneeded color-terminate code + retval = 0; + break; + } + if (retval >= bLeft) { + retval = 0; + } + if (retval > 0 && color) { + ptr += retval; + bLeft -= retval; + } + } + + } + if (bLeft > 0 && m_trace.prefix_f) { + va_list ap; + va_start(ap, fmt); + //find out length of body + size_t sz = 0; + sz = vsnprintf(NULL, 0, fmt, ap) + retval + (retval ? 4 : 0); + //add prefix string + retval = snprintf(ptr, bLeft, "%s", m_trace.prefix_f(sz)); + if (retval >= bLeft) { + retval = 0; + } + if (retval > 0) { + ptr += retval; + bLeft -= retval; + } + va_end(ap); + } + if (bLeft > 0) { + //add group tag + switch (dlevel) { + case (TRACE_LEVEL_ERROR): + retval = snprintf(ptr, bLeft, "[ERR ][%-4s]: ", grp); + break; + case (TRACE_LEVEL_WARN): + retval = snprintf(ptr, bLeft, "[WARN][%-4s]: ", grp); + break; + case (TRACE_LEVEL_INFO): + retval = snprintf(ptr, bLeft, "[INFO][%-4s]: ", grp); + break; + case (TRACE_LEVEL_DEBUG): + retval = snprintf(ptr, bLeft, "[DBG ][%-4s]: ", grp); + break; + default: + retval = snprintf(ptr, bLeft, " "); + break; + } + if (retval >= bLeft) { + retval = 0; + } + if (retval > 0) { + ptr += retval; + bLeft -= retval; + } + } + if (retval > 0 && bLeft > 0) { + va_list ap; + va_start(ap, fmt); + //add trace text + retval = vsnprintf(ptr, bLeft, fmt, ap); + if (retval >= bLeft) { + retval = 0; + } + if (retval > 0) { + ptr += retval; + bLeft -= retval; + } + va_end(ap); + } + + if (retval > 0 && bLeft > 0 && m_trace.suffix_f) { + //add suffix string + retval = snprintf(ptr, bLeft, "%s", m_trace.suffix_f()); + if (retval >= bLeft) { + retval = 0; + } + if (retval > 0) { + ptr += retval; + bLeft -= retval; + } + } + + if (retval > 0 && bLeft > 0 && color) { + //add zero color VT100 when color mode + retval = snprintf(ptr, bLeft, "\x1b[0m"); + if (retval >= bLeft) { + retval = 0; + } + if (retval > 0) { + // not used anymore + //ptr += retval; + //bLeft -= retval; + } + } + //print out whole data + m_trace.printf(m_trace.line); + } + //return tmp data pointer back to the beginning + mbed_trace_reset_tmp(); + } +} +static void mbed_trace_reset_tmp(void) +{ + m_trace.tmp_data_ptr = m_trace.tmp_data; +} +const char *mbed_trace_last(void) +{ + return m_trace.line; +} +/* Helping functions */ +#define tmp_data_left() m_trace.tmp_data_length-(m_trace.tmp_data_ptr-m_trace.tmp_data) +#if YOTTA_CFG_MTRACE_FEA_IPV6 == 1 +char *mbed_trace_ipv6(const void *addr_ptr) +{ + char *str = m_trace.tmp_data_ptr; + if (str == NULL) { + return ""; + } + if (tmp_data_left() < 41) { + return ""; + } + if (addr_ptr == NULL) { + return "<null>"; + } + str[0] = 0; + mbed_trace_ip6tos(addr_ptr, str); + m_trace.tmp_data_ptr += strlen(str) + 1; + return str; +} +char *mbed_trace_ipv6_prefix(const uint8_t *prefix, uint8_t prefix_len) +{ + char *str = m_trace.tmp_data_ptr; + int retval, bLeft = tmp_data_left(); + char tmp[40]; + uint8_t addr[16] = {0}; + + if (str == NULL) { + return ""; + } + if (bLeft < 45) { + return ""; + } + + if (prefix_len != 0) { + if (prefix == NULL || prefix_len > 128) { + return "<err>"; + } +#ifdef COMMON_FUNCTIONS_FN + bitcopy(addr, prefix, prefix_len); +#else + return ""; +#endif //COMMON_FUNCTIONS_FN + } + + mbed_trace_ip6tos(addr, tmp); + retval = snprintf(str, bLeft, "%s/%u", tmp, prefix_len); + if (retval <= 0 || retval > bLeft) { + return ""; + } + + m_trace.tmp_data_ptr += retval + 1; + return str; +} +#endif //YOTTA_CFG_MTRACE_FEA_IPV6 +char *mbed_trace_array(const uint8_t *buf, uint16_t len) +{ + int i, bLeft = tmp_data_left(); + char *str, *wptr; + str = m_trace.tmp_data_ptr; + if (str == NULL || bLeft == 0) { + return ""; + } + if (buf == NULL) { + return "<null>"; + } + wptr = str; + wptr[0] = 0; + const uint8_t *ptr = buf; + char overflow = 0; + for (i = 0; i < len; i++) { + if (bLeft <= 3) { + overflow = 1; + break; + } + int retval = snprintf(wptr, bLeft, "%02x:", *ptr++); + if (retval <= 0 || retval > bLeft) { + break; + } + bLeft -= retval; + wptr += retval; + } + if (wptr > str) { + if( overflow ) { + // replace last character as 'star', + // which indicate buffer len is not enough + *(wptr - 1) = '*'; + } else { + //null to replace last ':' character + *(wptr - 1) = 0; + } + } + m_trace.tmp_data_ptr = wptr; + return str; +}
diff -r fc16a32762df -r 506ad37c6bd7 source/mbed_trace_ip6tos.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/source/mbed_trace_ip6tos.c Thu Mar 31 18:10:53 2016 +0300 @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2014-2015 ARM Limited. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + * 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 <stdio.h> +#include <inttypes.h> +#include "mbed-trace/mbed_trace_ip6tos.h" + +/** + * Print binary IPv6 address to a string. + * String must contain enough room for full address, 40 bytes exact. + * IPv4 tunneling addresses are not covered. + * \param addr IPv6 address. + * \p buffer to write string to. + */ +void mbed_trace_ip6tos(const void *ip6addr, char *p) +{ + uint_fast8_t zero_start = 255, zero_len = 1; + const uint8_t *addr = ip6addr; + uint_fast16_t part; + + /* Follow RFC 5952 - pre-scan for longest run of zeros */ + for (uint_fast8_t n = 0; n < 8; n++) { + part = *addr++; + part = (part << 8) | *addr++; + if (part != 0) { + continue; + } + + /* We're at the start of a run of zeros - scan to non-zero (or end) */ + uint_fast8_t n0 = n; + for (n = n0 + 1; n < 8; n++) { + part = *addr++; + part = (part << 8) | *addr++; + if (part != 0) { + break; + } + } + + /* Now n0->initial zero of run, n->after final zero in run. Is this the + * longest run yet? If equal, we stick with the previous one - RFC 5952 + * S4.2.3. Note that zero_len being initialised to 1 stops us + * shortening a 1-part run (S4.2.2.) + */ + if (n - n0 > zero_len) { + zero_start = n0; + zero_len = n - n0; + } + + /* Continue scan for initial zeros from part n+1 - we've already + * consumed part n, and know it's non-zero. */ + } + + /* Now go back and print, jumping over any zero run */ + addr = ip6addr; + for (uint_fast8_t n = 0; n < 8;) { + if (n == zero_start) { + if (n == 0) { + *p++ = ':'; + } + *p++ = ':'; + addr += 2 * zero_len; + n += zero_len; + continue; + } + + part = *addr++; + part = (part << 8) | *addr++; + n++; + + p += sprintf(p, "%"PRIxFAST16, part); + + /* One iteration writes "part:" rather than ":part", and has the + * explicit check for n == 8 below, to allow easy extension for + * IPv4-in-IPv6-type addresses ("xxxx::xxxx:a.b.c.d"): we'd just + * run the same loop for 6 parts, and output would then finish with the + * required : or ::, ready for "a.b.c.d" to be tacked on. + */ + if (n != 8) { + *p++ = ':'; + } + } + *p++ = '\0'; +}