[log]: Normal log works on Linux now

* also removed strlcpy dependency from log
* added Kconfig option for linux target

Closes IDF-3245
This commit is contained in:
Jakob Hasse 2021-05-13 17:04:31 +08:00
parent 879e6d90c2
commit 0ea20caa71
11 changed files with 275 additions and 11 deletions

View File

@ -361,3 +361,10 @@ test_nvs_page:
- cd ${IDF_PATH}/components/nvs_flash/host_test/nvs_page_test
- idf.py build
- build/host_nvs_page_test.elf
test_log:
extends: .host_test_template
script:
- cd ${IDF_PATH}/components/log/host_test/log_test
- idf.py build
- build/test_log_host.elf

View File

@ -64,6 +64,10 @@ mainmenu "Espressif IoT Development Framework Configuration"
select FREERTOS_UNICORE
select IDF_TARGET_ARCH_RISCV
config IDF_TARGET_LINUX
bool
default "y" if IDF_TARGET="linux"
config IDF_FIRMWARE_CHIP_ID
hex
default 0x0000 if IDF_TARGET_ESP32

View File

@ -1,15 +1,23 @@
list(APPEND srcs "log.c"
"log_buffers.c")
idf_build_get_property(target IDF_TARGET)
set(srcs "log.c")
if(${target} STREQUAL "linux")
# We leave log buffers out for now on Linux since it's rarely used
list(APPEND srcs "log_linux.c")
else()
list(APPEND srcs "log_buffers.c")
list(APPEND priv_requires soc)
endif()
idf_component_register(SRCS ${srcs}
INCLUDE_DIRS "include"
LDFRAGMENTS linker.lf
PRIV_REQUIRES soc)
PRIV_REQUIRES ${priv_requires})
idf_build_get_property(build_components BUILD_COMPONENTS)
# Ideally, FreeRTOS shouldn't be included into bootloader build, so the 2nd check should be unnecessary
if(freertos IN_LIST BUILD_COMPONENTS AND NOT BOOTLOADER_BUILD)
target_sources(${COMPONENT_TARGET} PRIVATE log_freertos.c)
else()
target_sources(${COMPONENT_TARGET} PRIVATE log_noos.c)
if(NOT ${target} STREQUAL "linux")
# Ideally, FreeRTOS shouldn't be included into bootloader build, so the 2nd check should be unnecessary
if(freertos IN_LIST BUILD_COMPONENTS AND NOT BOOTLOADER_BUILD)
target_sources(${COMPONENT_TARGET} PRIVATE log_freertos.c)
else()
target_sources(${COMPONENT_TARGET} PRIVATE log_noos.c)
endif()
endif()

View File

@ -0,0 +1,5 @@
cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
set(COMPONENTS main)
project(test_log_host)

View File

@ -0,0 +1,38 @@
| Supported Targets | Linux |
| ----------------- | ----- |
# Simple log test on Linux target
This unit test tests basic functionality of the log component. The test does not use mocks. Instead, it runs the whole implementation of the component on the Linux host. The test framework is CATCH.
*Note that the early log (ESP_EARLY_LOG<X>) functionality has not been ported to Linux since it depends on the ROM component.*
## Requirements
* A Linux system
* The usual IDF requirements for Linux system, as described in the [Getting Started Guides](../../../../docs/en/get-started/index.rst).
* The host's gcc/g++
This application has been tested on Ubuntu 20.04 with `gcc` version *9.3.0*.
## Build
First, make sure that the target is set to Linux. Run `idf.py --preview set-target linux` if you are not sure. Then do a normal IDF build: `idf.py build`.
## Run
IDF monitor doesn't work yet for Linux. You have to run the app manually:
```bash
./build/test_log_host.elf
```
## Example Output
Ideally, all tests pass, which is indicated by "All tests passed" in the last line:
```bash
$ ./build/test_log_host.elf
===============================================================================
All tests passed (8 assertions in 6 test cases)
```

View File

@ -0,0 +1,5 @@
idf_component_register(SRCS "log_test.cpp"
INCLUDE_DIRS
"."
$ENV{IDF_PATH}/tools/catch
REQUIRES log)

View File

@ -0,0 +1,141 @@
/* LOG unit tests
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#define CATCH_CONFIG_MAIN
#include <cstdio>
#include <regex>
#include <iostream>
#include "esp_log.h"
#include "catch.hpp"
using namespace std;
static const char *TEST_TAG = "test";
struct PrintFixture {
static const size_t BUFFER_SIZE = 4096;
PrintFixture(esp_log_level_t log_level = ESP_LOG_VERBOSE)
{
if (instance != nullptr) {
throw exception();
}
std::memset(print_buffer, 0, BUFFER_SIZE);
instance = this;
old_vprintf = esp_log_set_vprintf(print_callback);
esp_log_level_set(TEST_TAG, log_level);
}
~PrintFixture()
{
esp_log_level_set(TEST_TAG, ESP_LOG_INFO);
esp_log_set_vprintf(old_vprintf);
instance = nullptr;
}
string get_print_buffer_string() const
{
return string(print_buffer);
}
void reset_buffer()
{
std::memset(print_buffer, 0, BUFFER_SIZE);
}
char print_buffer [BUFFER_SIZE];
private:
static int print_callback(const char *format, va_list args)
{
return instance->print_to_buffer(format, args);
}
int print_to_buffer(const char *format, va_list args)
{
int ret = vsnprintf(print_buffer, BUFFER_SIZE, format, args);
return ret;
}
static PrintFixture *instance;
vprintf_like_t old_vprintf;
};
PrintFixture *PrintFixture::instance = nullptr;
TEST_CASE("verbose log level")
{
PrintFixture fix(ESP_LOG_VERBOSE);
const std::regex test_print("V \\([0-9]*\\) test: verbose", std::regex::ECMAScript);
ESP_LOGV(TEST_TAG, "verbose");
CHECK(regex_search(fix.get_print_buffer_string(), test_print) == true);
}
TEST_CASE("debug log level")
{
PrintFixture fix(ESP_LOG_DEBUG);
const std::regex test_print("D \\([0-9]*\\) test: debug", std::regex::ECMAScript);
ESP_LOGD(TEST_TAG, "debug");
CHECK(regex_search(fix.get_print_buffer_string(), test_print) == true);
}
TEST_CASE("info log level")
{
PrintFixture fix(ESP_LOG_INFO);
const std::regex test_print("I \\([0-9]*\\) test: info", std::regex::ECMAScript);
ESP_LOGI(TEST_TAG, "info");
CHECK(regex_search(fix.get_print_buffer_string(), test_print) == true);
}
TEST_CASE("warn log level")
{
PrintFixture fix(ESP_LOG_WARN);
const std::regex test_print("W \\([0-9]*\\) test: warn", std::regex::ECMAScript);
ESP_LOGW(TEST_TAG, "warn");
CHECK(regex_search(fix.get_print_buffer_string(), test_print) == true);
}
TEST_CASE("error log level")
{
PrintFixture fix(ESP_LOG_ERROR);
const std::regex test_print("E \\([0-9]*\\) test: error", std::regex::ECMAScript);
ESP_LOGE(TEST_TAG, "error");
CHECK(regex_search(fix.get_print_buffer_string(), test_print) == true);
}
TEST_CASE("changing log level")
{
PrintFixture fix(ESP_LOG_INFO);
const std::regex test_print("I \\([0-9]*\\) test: must indeed be printed", std::regex::ECMAScript);
ESP_LOGI(TEST_TAG, "must indeed be printed");
CHECK(regex_search(fix.get_print_buffer_string(), test_print) == true);
fix.reset_buffer();
esp_log_level_set(TEST_TAG, ESP_LOG_WARN);
ESP_LOGI(TEST_TAG, "must not be printed");
CHECK(fix.get_print_buffer_string().size() == 0);
fix.reset_buffer();
esp_log_level_set(TEST_TAG, ESP_LOG_INFO);
ESP_LOGI(TEST_TAG, "must indeed be printed");
CHECK(regex_search(fix.get_print_buffer_string(), test_print) == true);
}

View File

@ -0,0 +1,7 @@
CONFIG_IDF_TARGET="linux"
CONFIG_COMPILER_CXX_EXCEPTIONS=y
CONFIG_LOG_TIMESTAMP_SOURCE_RTOS=y
CONFIG_LOG_DEFAULT_LEVEL_VERBOSE=y
CONFIG_LOG_DEFAULT_LEVEL=5
CONFIG_LOG_MAXIMUM_LEVEL=5
CONFIG_LOG_MAXIMUM_EQUALS_DEFAULT=y

View File

@ -9,8 +9,10 @@
#include <stdint.h>
#include <stdarg.h>
#include "esp_rom_sys.h"
#include "sdkconfig.h"
#if !defined(CONFIG_IDF_TARGET_LINUX)
#include "esp_rom_sys.h"
#endif // !CONFIG_IDF_TARGET_LINUX
#if CONFIG_IDF_TARGET_ESP32
#include "esp32/rom/ets_sys.h" // will be removed in idf v5.0
#elif CONFIG_IDF_TARGET_ESP32S2

View File

@ -30,6 +30,7 @@
#include <stdbool.h>
#include <stdarg.h>
#include <stddef.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
@ -123,7 +124,7 @@ void esp_log_level_set(const char *tag, esp_log_level_t level)
return;
}
new_entry->level = (uint8_t) level;
strlcpy(new_entry->tag, tag, tag_len);
memcpy(new_entry->tag, tag, tag_len); // we know the size and strncpy would trigger a compiler warning here
SLIST_INSERT_HEAD(&s_log_tags, new_entry, entries);
}

View File

@ -0,0 +1,46 @@
// Copyright 2010-2021 Espressif Systems (Shanghai) CO LTD
//
// 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 <pthread.h>
#include <time.h>
#include <assert.h>
#include <stdint.h>
#include "esp_log_private.h"
static pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER;
void esp_log_impl_lock(void)
{
assert(pthread_mutex_lock(&mutex1) == 0);
}
bool esp_log_impl_lock_timeout(void)
{
esp_log_impl_lock();
return true;
}
void esp_log_impl_unlock(void)
{
assert(pthread_mutex_unlock(&mutex1) == 0);
}
uint32_t esp_log_timestamp(void)
{
struct timespec current_time;
int result = clock_gettime(CLOCK_MONOTONIC, &current_time);
assert(result == 0);
uint32_t seconds = current_time.tv_sec * 1000 + current_time.tv_nsec / 1000000;
return seconds;
}