third_party.pigweed.src/pw_assert_basic/basic_handler.cc
Nathaniel Brough be3fba1ec8 pw_assert_basic: Fix build and minor bugfix
This commit fixes the bazel build files. This fix also addresses a bug
where an interaction between @io_bazel//src/main/tools:process-wrapper
and pw_assert_basic caused the output from `bazel test
//some_test_target` to be completely empty on assertion. This occured
because pw_assert_basic was 'aborting' causing the core to dump without
calling the destructors. Under normal circumstances this is fine as
stdout is synchronised. However as bazel's process wrapper binds
stdout, it results in stdout buffering, meaning that when
pw_assert_basic 'aborts' the buffered stdout is lost. The replacement
of 'abort' to 'exit' allows destructors for the stdout handles to be
called thus flushing the buffer out to bazels process-wrapper.
This is not a critical bug and is more of a nuisance as the message that
prints the line number that caused the assertion is lost when the
program aborts.

Change-Id: Ia1599db78c5caa878f3d7496e2b3009e3c44dcea
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/34621
Reviewed-by: Keir Mierle <keir@google.com>
Reviewed-by: Wyatt Hepler <hepler@google.com>
Commit-Queue: Keir Mierle <keir@google.com>
2021-03-09 18:59:11 +00:00

172 lines
6.1 KiB
C++

// Copyright 2021 The Pigweed Authors
//
// 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
//
// https://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.
// This is a very basic direct output log implementation with no buffering.
//#define PW_LOG_MODULE_NAME "ASRT"
//#include "pw_log/log.h"
#include <cstring>
#include "pw_assert/options.h"
#include "pw_assert_basic/handler.h"
#include "pw_preprocessor/util.h"
#include "pw_string/string_builder.h"
#include "pw_sys_io/sys_io.h"
// If 1, call C's standard abort() function on assert failure.
#ifndef PW_ASSERT_BASIC_ABORT
#define PW_ASSERT_BASIC_ABORT 1
#endif // PW_ASSERT_BASIC_ABORT
// TODO(pwbug/17): Expose these through the config system.
#define PW_ASSERT_BASIC_SHOW_BANNER 1
#define PW_ASSERT_BASIC_USE_COLORS 1
// ANSI color constants to control the terminal. Not Windows compatible.
// clang-format off
#if PW_ASSERT_BASIC_USE_COLORS
#define MAGENTA "\033[35m"
#define YELLOW "\033[33m"
#define RED "\033[31m"
#define GREEN "\033[32m"
#define BLUE "\033[96m"
#define BLACK "\033[30m"
#define YELLOW_BG "\033[43m"
#define WHITE_BG "\033[47m"
#define RED_BG "\033[41m"
#define BOLD "\033[1m"
#define RESET "\033[0m"
#else
#define MAGENTA ""
#define YELLOW ""
#define RED ""
#define GREEN ""
#define BLUE ""
#define BLACK ""
#define YELLOW_BG ""
#define WHITE_BG ""
#define RED_BG ""
#define BOLD ""
#define RESET ""
#endif // PW_ASSERT_BASIC_USE_COLORS
// clang-format on
static const char* kCrashBanner[] = {
" ",
" ▄████▄ ██▀███ ▄▄▄ ██████ ██░ ██ ",
" ▒██▀ ▀█ ▓██ ▒ ██▒ ▒████▄ ▒██ ▒ ▓██░ ██▒ ",
" ▒▓█ 💥 ▄ ▓██ ░▄█ ▒ ▒██ ▀█▄ ░ ▓██▄ ▒██▀▀██░ ",
" ▒▓▓▄ ▄██▒ ▒██▀▀█▄ ░██▄▄▄▄██ ▒ ██▒ ░▓█ ░██ ",
" ▒ ▓███▀ ░ ░██▓ ▒██▒ ▓█ ▓██▒ ▒██████▒▒ ░▓█▒░██▓ ",
" ░ ░▒ ▒ ░ ░ ▒▓ ░▒▓░ ▒▒ ▓▒█░ ▒ ▒▓▒ ▒ ░ ▒ ░░▒░▒ ",
" ░ ▒ ░▒ ░ ▒░ ▒ ▒▒ ░ ░ ░▒ ░ ░ ▒ ░▒░ ░ ",
" ░ ░░ ░ ░ ▒ ░ ░ ░ ░ ░░ ░ ",
" ░ ░ ░ ░ ░ ░ ░ ░ ░ ",
"",
" ",
};
using pw::sys_io::WriteLine;
typedef pw::StringBuffer<150> Buffer;
extern "C" void pw_assert_basic_HandleFailure(const char* file_name,
int line_number,
const char* function_name,
const char* format,
...) {
// As a matter of usability, crashes should be visible; make it so.
#if PW_ASSERT_BASIC_SHOW_BANNER
WriteLine(RED);
for (const char* line : kCrashBanner) {
WriteLine(line);
}
WriteLine(RESET);
#endif // PW_ASSERT_BASIC_SHOW_BANNER
WriteLine(
" Welp, that didn't go as planned. "
"It seems we crashed. Terribly sorry!");
WriteLine("");
WriteLine(YELLOW " CRASH MESSAGE" RESET);
WriteLine("");
{
Buffer buffer;
buffer << " ";
va_list args;
va_start(args, format);
buffer.FormatVaList(format, args);
va_end(args);
WriteLine(buffer.view());
}
if (file_name != nullptr && line_number != -1) {
WriteLine("");
WriteLine(YELLOW " CRASH FILE & LINE" RESET);
WriteLine("");
{
Buffer buffer;
buffer.Format(" %s:%d", file_name, line_number);
WriteLine(buffer.view());
}
}
if (function_name != nullptr) {
WriteLine("");
WriteLine(YELLOW " CRASH FUNCTION" RESET);
WriteLine("");
{
Buffer buffer;
buffer.Format(" %s", function_name);
WriteLine(buffer.view());
}
}
// TODO(pwbug/95): Perhaps surprisingly, this doesn't actually crash the
// device. At some point we'll have a reboot BSP function or similar, but for
// now this is acceptable since no one is using this basic backend.
if (!PW_ASSERT_BASIC_DISABLE_NORETURN) {
if (PW_ASSERT_BASIC_ABORT) {
// Using exit() instead of abort() here because exit() allows for the
// destructors for the stdout buffers to be called. This addresses an
// issue that occurs when Bazel's execution wrapper binds stdout. This
// results in stdout going from a synchronized to a buffered file
// descriptor. In this case when abort() is called in a Bazel test the
// program exits before the stdout buffer can be synchronized with Bazel's
// execution wrapper, the resulting output from a test is an empty output
// buffer. Using exit() here allows the destructors to synchronized the
// stdout buffer before exiting.
exit(1);
} else {
WriteLine("");
WriteLine(MAGENTA " HANG TIME" RESET);
WriteLine("");
WriteLine(
" ... until a debugger joins. System is waiting in a while(1)");
while (1) {
}
}
PW_UNREACHABLE;
} else {
WriteLine("");
WriteLine(MAGENTA " NOTE: YOU ARE IN ASSERT BASIC TEST MODE" RESET);
WriteLine("");
WriteLine(" This build returns from the crash handler for testing.");
WriteLine(" If you see this message in production, your build is ");
WriteLine(" incorrectly configured. Search for");
WriteLine(" PW_ASSERT_BASIC_DISABLE_NORETURN to fix it.");
WriteLine("");
}
}