Import of the watch repository from Pebble

This commit is contained in:
Matthieu Jeanson 2024-12-12 16:43:03 -08:00 committed by Katharine Berry
commit 3b92768480
10334 changed files with 2564465 additions and 0 deletions

View file

@ -0,0 +1,224 @@
/*
* Copyright 2024 Google LLC
*
* 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.
*/
#pragma once
#include <logging/log_hashing.h>
/*
* This file defines the structures required for Binary Logging. Please see
* https://docs.google.com/document/d/1AyRGwr8CiilAViha56EiuRSZFiW0fWMcsTfzkNxByZ8
* for more information.
*/
// SLIP Framing (if not using PULSE). Packet is: END, <packet>, <crc32>, END
#define END 0xC0
#define ESC 0xDB
#define ESC_END 0xDC
#define ESC_ESC 0xDD
// Version
typedef struct BinLogMessage_Version {
union {
struct {
uint8_t reserved:4;
uint8_t unhashed_msg:1;
uint8_t parameterized:1;
uint8_t tick_count:1;
uint8_t time_date:1;
};
uint8_t version;
};
} BinLogMessage_Version;
#define BINLOGMSG_VERSION_UNHASHED_MSG (1 << 3)
#define BINLOGMSG_VERSION_PARAMETERIZED (1 << 2)
#define BINLOGMSG_VERSION_TICK_COUNT (1 << 1)
#define BINLOGMSG_VERSION_TIME_DATE (1 << 0)
_Static_assert(sizeof(BinLogMessage_Version) == 1, "BinLogMessage_Version size != 1");
// Time_Full
// The times are in UTC. All values are 0 based (i.e., hour is [0,23], minute is [0,59],
// second is [0,59], millisecond is [0,999]).
typedef struct Time_Full {
uint32_t reserved:5;
uint32_t hour:5;
uint32_t minute:6;
uint32_t second:6;
uint32_t millisecond:10;
} Time_Full;
// Time_Tick
// Total ticks = (count_high << 32) | count.
typedef struct Time_Tick {
uint16_t reserved;
uint16_t count_high;
uint32_t count;
} Time_Tick;
// Date
// The year is an offset from 2000 (e.g. year = 16 is 2016).
// All remaining values are 1 based (i.e., month is [1,12], day is [1,31]).
// An invalid/unknown date is identified by (year, month, day) = (0, 0, 0). Thus, Date = 0 is an
// invalid date, not the start of the epoch, which would be (0, 1, 1).
typedef struct Date {
uint16_t year:7;
uint16_t month:4;
uint16_t day:5;
} Date;
// MessageID
typedef struct MessageID {
union {
struct {
uint32_t msg_number:19; // LSB
uint32_t task_id:4;
uint32_t str_index_1:3;
uint32_t str_index_2:3;
uint32_t reserved:1;
uint32_t core_number:2; // MSB
};
uint32_t msg_id;
};
} MessageID;
_Static_assert(sizeof(MessageID) == 4, "MessageID size != 4");
typedef struct BinLogMessage_Header {
uint8_t version;
uint8_t length;
} BinLogMessage_Header;
typedef struct BinLogMessage_Header_v0 {
uint8_t version;
uint8_t length;
uint8_t reserved[2];
} BinLogMessage_Header_v0;
#define BINLOGMSG_VERSION_HEADER_V0 (0)
typedef struct BinLogMessage_Header_v1 {
uint8_t version;
uint8_t length;
Date date;
Time_Full time;
} BinLogMessage_Header_v1;
#define BINLOGMSG_VERSION_HEADER_V1 (BINLOGMSG_VERSION_TIME_DATE)
typedef struct BinLogMessage_Header_v2 {
uint8_t version;
uint8_t length;
uint8_t reserved[2];
Time_Tick tick_count;
} BinLogMessage_Header_v2;
#define BINLOGMSG_VERSION_HEADER_V2 (BINLOGMSG_VERSION_TICK_COUNT)
typedef struct BinLogMessage_Header_v3 {
uint8_t version;
uint8_t length;
Date date;
Time_Full time;
Time_Tick tick_count;
} BinLogMessage_Header_v3;
#define BINLOGMSG_VERSION_HEADER_V3 (BINLOGMSG_VERSION_TIME_DATE | BINLOGMSG_VERSION_TICK_COUNT)
typedef struct BinLogMessage_ParamBody {
MessageID msgid;
uint32_t payload[0];
} BinLogMessage_ParamBody;
typedef struct BinLogMessage_StringParam {
uint8_t length;
uint8_t string[0]; // string[length]
// uint8_t padding[((length + sizeof(length) + 3) % 4)]
} BinLogMessage_StringParam;
typedef uint32_t BinLogMessage_IntParam;
typedef struct BinLogMessage_UnhashedBody {
uint16_t line_number;
uint8_t filename[16];
uint8_t reserved:2;
uint8_t core_number:2;
uint8_t task_id:4;
uint8_t level;
uint8_t length;
uint8_t string[0]; // string[length];
// uint8_t padding[];
} BinLogMessage_UnhashedBody;
/*
int len = MAX(strlen(log_string), 255 - sizeof(BinLogMessage_Header_vX))
typedef struct BinLogMessage_SimpleBody {
uint8_t string[len];
uint8_t padding[((sizeof(BinLogMessage_Header_vX) + len + 3) % 4)];
} BinLogMessage_SimpleBody;
*/
typedef struct BinLogMessage_Param_v0 {
BinLogMessage_Header_v0 header;
BinLogMessage_ParamBody body;
} BinLogMessage_Param_v0;
#define BINLOGMSG_VERSION_PARAM_V0 (BINLOGMSG_VERSION_HEADER_V0 | BINLOGMSG_VERSION_PARAMETERIZED)
typedef struct BinLogMessage_Param_v1 {
BinLogMessage_Header_v1 header;
BinLogMessage_ParamBody body;
} BinLogMessage_Param_v1;
#define BINLOGMSG_VERSION_PARAM_V1 (BINLOGMSG_VERSION_HEADER_V1 | BINLOGMSG_VERSION_PARAMETERIZED)
typedef struct BinLogMessage_Param_v2 {
BinLogMessage_Header_v2 header;
BinLogMessage_ParamBody body;
} BinLogMessage_Param_v2;
#define BINLOGMSG_VERSION_PARAM_V2 (BINLOGMSG_VERSION_HEADER_V2 | BINLOGMSG_VERSION_PARAMETERIZED)
typedef struct BinLogMessage_Param_v3 {
BinLogMessage_Header_v3 header;
BinLogMessage_ParamBody body;
} BinLogMessage_Param_v3;
#define BINLOGMSG_VERSION_PARAM_V3 (BINLOGMSG_VERSION_HEADER_V3 | BINLOGMSG_VERSION_PARAMETERIZED)
typedef struct BinLogMessage_Unhashed_v0 {
BinLogMessage_Header_v0 header;
BinLogMessage_UnhashedBody body;
} BinLogMessage_Unhashed_v0;
#define BINLOGMSG_VERSION_UNHASHED_V0 (BINLOGMSG_VERSION_HEADER_V0 | BINLOGMSG_VERSION_UNHASHED_MSG)
typedef struct BinLogMessage_Unhashed_v1 {
BinLogMessage_Header_v1 header;
BinLogMessage_UnhashedBody body;
} BinLogMessage_Unhashed_v1;
#define BINLOGMSG_VERSION_UNHASHED_V1 (BINLOGMSG_VERSION_HEADER_V1 | BINLOGMSG_VERSION_UNHASHED_MSG)
typedef struct BinLogMessage_Unhashed_v2 {
BinLogMessage_Header_v2 header;
BinLogMessage_UnhashedBody body;
} BinLogMessage_Unhashed_v2;
#define BINLOGMSG_VERSION_UNHASHED_V2 (BINLOGMSG_VERSION_HEADER_V2 | BINLOGMSG_VERSION_UNHASHED_MSG)
typedef struct BinLogMessage_Unhashed_v3 {
BinLogMessage_Header_v3 header;
BinLogMessage_UnhashedBody body;
} BinLogMessage_Unhashed_v3;
#define BINLOGMSG_VERSION_UNHASHED_V3 (BINLOGMSG_VERSION_HEADER_V3 | BINLOGMSG_VERSION_UNHASHED_MSG)
typedef struct BinLogMessage_String_v1 {
BinLogMessage_Header_v1 header;
uint8_t string[0];
} BinLogMessage_String_v1;
#define BINLOGMSG_VERSION_STRING_V1 (BINLOGMSG_VERSION_HEADER_V1)

View file

@ -0,0 +1,232 @@
/*
* Copyright 2024 Google LLC
*
* 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.
*/
/************************************************************************************************
* New Logging
*
* The NewLogging system provides in place hashing during the compile stage of building, removing
* logging strings from the source code and replacing them with a unique token, conserving space in
* the firmware.
*
* The unique token is (well, see below) actually just a pointer to a new section in the .elf
* named .log_strings. The .log_strings section is mapped to an unused portion of memory and
* isn't compiled into the final firmware binary image.
*
* Token format:
* The token is acually a packed uint32_t. The format is as follows:
* 31-29: num fmt conversions [0-7]
* 28-26: string index 2 [0-7], 1 based. 0 if no second string. 1-7 otherwise
* 25-23: string index 1 [0-7], 1 based. 0 if no first string. 1-7 otherwise
* 22-20: log level [0-5] mapped onto LOG_LEVEL_ALWAYS through LOG_LEVEL_DEBUG_VERBOSE
* 19: reserved
* 18- 0: Offset info .log_strings section. This allows 512 KB of strings.
*
* Note: it might not be necessary to use so many bits for the log level. Dynamic flitering might
* not be so important, and 'log to flash' could be 1 bit, or Curried to a set of function calls.
* These changes would require more work in the logging infrastructure.
*
* .log_strings Section is formatted as follows:
* - .log_string.header: "NL<M><m>:<offset-mask>=<token-list>"
* where:
* - <M> is XX major version -- increase means not backwards compatible change
* - <m> is YY minor version -- increase means backwards compatible change
* - <offset-mask> defines the number of bits used in the token for the section offset
* - <token-list>: <token>:<token-list>
* : '\0'
* - <token>: <file>
* : <line>
* : <level>
* : <color>
* : <fmt>
* - .log_core_number: "CORE<C>"
* where:
* - <C> is the core number. This will be two bits.
* For now, the primary core will be 00; the Bluetooth chip will be 01.
* These definitions will be different for every system -- all that matters is that they're
* internally consistent.
* - .log_string
* which is a list of <token-list> representing the log strings from the source code.
*
* Note: this code must be compiled with -Os or the codesize will explode!
*
* Limitations:
* - maximum 7 format conversions per print
* - maximum 2 string conversions per print
* - string parameters may not be flagged or formatted in any way --'%s' only.
* - printing the '%' is not supported -- '%%' is not allowed.
* - only 32 bit (or fewer) parameters currently supported automatically. Multi-word parameters
* require special handling.
* - errors are not automatically detected. This will have to be done later by a script. Sorry.
*
* Extensions (to be implemented at some point):
* - Colour groups/overrides
* - MAC/BT address print: format specifier: %M/m
* - ENUM print: %u[<enum name>]
*
* LogHash.dict:
* See https://pebbletechnology.atlassian.net/wiki/display/DEV/New+Logging.
***********************************************************************************************/
#pragma once
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "util/attributes.h"
#define NEW_LOG_VERSION "0101"
#define LOG_STRINGS_SECTION_ADDRESS 0xC0000000
#define PACKED_CORE_OFFSET 30 // 2 bits - Core number
#define PACKED_CORE_MASK 0x03
#define PACKED_NUM_FMT_OFFSET 29 // 3 bits - Number format conversions
#define PACKED_NUM_FMT_MASK 0x07
#define PACKED_STR1FMT_OFFSET 26 // 3 bits - indicies of string parameter 1 format conversion
#define PACKED_STR1FMT_MASK 0x07
#define PACKED_STR2FMT_OFFSET 23 // 3 bits - indicies of string parameter 2 format conversion
#define PACKED_STR2FMT_MASK 0x07
#define PACKED_STRFMTS_OFFSET 23 // 6 bits - indicies of string parameters 1 & 2.
#define PACKED_STRFMTS_MASK 0x3f
#define PACKED_LEVEL_OFFSET 20 // 3 bits - log level
#define PACKED_LEVEL_MASK 0x07
#define PACKED_HASH_OFFSET 0
#define PACKED_HASH_MASK 0x7FFFF // 19 bits - string table offset (512 KB)
#define MSGID_STR_AND_HASH_MASK ((PACKED_STRFMTS_MASK << PACKED_STRFMTS_OFFSET) | \
(PACKED_HASH_MASK << PACKED_HASH_OFFSET))
#define MSGID_CORE_AND_HASH_MASK ((PACKED_CORE_MASK << PACKED_CORE_OFFSET) | \
(PACKED_HASH_MASK << PACKED_HASH_OFFSET))
#ifndef STRINGIFY
#define STRINGIFY_NX(a) #a
#define STRINGIFY(a) STRINGIFY_NX(a)
#endif // STRINGIFY
/* Printf Format argument checking.
*
* NB: it's critical that the 'if (false)' tag is included before the call to
* PBL_LOG_x_printf_arg_check(). Without this obviously useless check, PBL_LOG_x_printf_arg_check()
* would not be optimised out and cause a) a linker error (missing function body), b) take up
* code space & time, and c) would cause the arguments passed to PBL_LOG (NEW_LOG_HASH) to be
* evaluated twice. This is fine with normal parameters, but could result in macros or functions
* being called twice and messing up globals in unexpected ways.
*/
void PBL_LOG_x_printf_arg_check(const char *fmt, ...) FORMAT_PRINTF(1, 2);
#define NEW_LOG_HASH(logfunc, level, color, fmt, ...) \
{ \
static const char str[] __attribute__((nocommon, section(".log_strings"))) = \
__FILE__ ":" STRINGIFY(__LINE__) ":" STRINGIFY(level) ":" color ":" fmt; \
logfunc((uint32_t)&str[LOG_SECTION_OFFSET(level, fmt)], ##__VA_ARGS__); \
if (0) PBL_LOG_x_printf_arg_check(fmt, ##__VA_ARGS__); \
}
ALWAYS_INLINE static uint32_t LOG_SECTION_OFFSET(const uint8_t level, const char *fmt) {
const char *p1 = NULL, *p2 = NULL, *p3 = NULL, *p4 = NULL;
const char *p5 = NULL, *p6 = NULL, *p7 = NULL, *p8 = NULL;
const char *s1 = NULL, *s2 = NULL, *s3 = NULL, *s4 = NULL;
const char *s5 = NULL, *s6 = NULL, *s7 = NULL;
// Search for % characters in fmt. p1-p8 point to the character immediately succeeding the first
// 8 % characters in fmt (or NULL, if there aren't 8 % characters in fmt).
p1 = __builtin_strchr(fmt, '%') ? (__builtin_strchr(fmt, '%') + 1) : NULL;
if (p1) p2 = __builtin_strchr(p1, '%') ? (__builtin_strchr(p1, '%') + 1) : NULL;
if (p2) p3 = __builtin_strchr(p2, '%') ? (__builtin_strchr(p2, '%') + 1) : NULL;
if (p3) p4 = __builtin_strchr(p3, '%') ? (__builtin_strchr(p3, '%') + 1) : NULL;
if (p4) p5 = __builtin_strchr(p4, '%') ? (__builtin_strchr(p4, '%') + 1) : NULL;
if (p5) p6 = __builtin_strchr(p5, '%') ? (__builtin_strchr(p5, '%') + 1) : NULL;
if (p6) p7 = __builtin_strchr(p6, '%') ? (__builtin_strchr(p6, '%') + 1) : NULL;
if (p7) p8 = __builtin_strchr(p7, '%') ? (__builtin_strchr(p7, '%') + 1) : NULL;
// Check that fmt doesn't contain the escaped % symbol, '%%'. It's too hard to handle correctly
// in every case.
if ((p1 + 1 == p2) || (p2 + 1 == p3) || (p3 + 1 == p4) || (p4 + 1 == p5) ||
(p5 + 1 == p6) || (p6 + 1 == p7) || (p7 + 1 == p8)) {
return 0;
}
// Count number of valid pointers by bool-inversion-twice
uint8_t num_params = !!p1 + !!p2 + !!p3 + !!p4 + !!p5 + !!p6 + !!p7 + !!p8;
// Check that there aren't more than 7 format conversions. We have only 3 bits per string index.
if (num_params > 7) {
return 0;
}
// Search for an 's' character succeeding the % characters in fmt. s1-s7 point to the first 's'
// charactres in fmt after the previously found % characters (or NULL if there aren't 7 's'
// characters in fmt).
if (p1) s1 = __builtin_strchr(p1, 's');
if (p2) s2 = __builtin_strchr(p2, 's');
if (p3) s3 = __builtin_strchr(p3, 's');
if (p4) s4 = __builtin_strchr(p4, 's');
if (p5) s5 = __builtin_strchr(p5, 's');
if (p6) s6 = __builtin_strchr(p6, 's');
if (p7) s7 = __builtin_strchr(p7, 's');
// See if the 's' characters immediately succeed the '%' characters. If so, set flag psX.
const int ps1 = p1 ? (p1 == s1) : 0;
const int ps2 = p2 ? (p2 == s2) : 0;
const int ps3 = p3 ? (p3 == s3) : 0;
const int ps4 = p4 ? (p4 == s4) : 0;
const int ps5 = p5 ? (p5 == s5) : 0;
const int ps6 = p6 ? (p6 == s6) : 0;
const int ps7 = p7 ? (p7 == s7) : 0;
// Count the number of '%s' parameters
const int num_s_params = ps1 + ps2 + ps3 + ps4 + ps5 + ps6 + ps7;
// We currently support only 2 string parameters.
if (num_s_params > 2) {
return 0;
}
// Format the (maximum) two string parameter indicies as:
// (1-based index of first %s << 3) | (1-based index of second %s << 0)
// If there is only one %s parameter, the index will be formatted as:
// (1-based index of first %s << 0)
const int a1 = ps1 ? 1 : 0;
const int a2 = ps2 ? (a1 << 3) + 2 : a1;
const int a3 = ps3 ? (a2 << 3) + 3 : a2;
const int a4 = ps4 ? (a3 << 3) + 4 : a3;
const int a5 = ps5 ? (a4 << 3) + 5 : a4;
const int a6 = ps6 ? (a5 << 3) + 6 : a5;
const int a7 = ps7 ? (a6 << 3) + 7 : a6;
const int string_indicies = a7;
// Convert level to packed_level
int packed_level = LOG_LEVEL_ALWAYS;
if (level == LOG_LEVEL_ERROR) {
packed_level = 1;
} else if (level == LOG_LEVEL_WARNING) {
packed_level = 2;
} else if (level == LOG_LEVEL_INFO) {
packed_level = 3;
} else if (level == LOG_LEVEL_DEBUG) {
packed_level = 4;
} else if (level == LOG_LEVEL_DEBUG_VERBOSE) {
packed_level = 5;
}
const uint32_t offset = (((num_params & PACKED_NUM_FMT_MASK) << PACKED_NUM_FMT_OFFSET) |
((packed_level & PACKED_LEVEL_MASK) << PACKED_LEVEL_OFFSET) |
((string_indicies & PACKED_STRFMTS_MASK) << PACKED_STRFMTS_OFFSET));
return (offset - LOG_STRINGS_SECTION_ADDRESS);
}