forked from bartvdbraak/blender
Tests: fail automated tests on memory leaks and other internal errors
This adds a new `--debug-exit-on-error` flag. When it is set, Blender will abort with a non-zero exit code when there are internal errors. Currently, "internal errors" includes memory leaks detected by guardedalloc and error/fatal log entries in clog. The new flag is passed to Blender in various places where automated tests are run. Furthermore, the `--debug-memory` flag is used in tests, because that makes the verbose output more useful, when dealing with memory leaks. Reviewers: brecht, sergey Differential Revision: https://developer.blender.org/D8665
This commit is contained in:
parent
d8cf6ee316
commit
8a9912eaf8
@ -139,6 +139,7 @@ void CLG_exit(void);
|
|||||||
void CLG_output_set(void *file_handle);
|
void CLG_output_set(void *file_handle);
|
||||||
void CLG_output_use_basename_set(int value);
|
void CLG_output_use_basename_set(int value);
|
||||||
void CLG_output_use_timestamp_set(int value);
|
void CLG_output_use_timestamp_set(int value);
|
||||||
|
void CLG_error_fn_set(void (*error_fn)(void *file_handle));
|
||||||
void CLG_fatal_fn_set(void (*fatal_fn)(void *file_handle));
|
void CLG_fatal_fn_set(void (*fatal_fn)(void *file_handle));
|
||||||
void CLG_backtrace_fn_set(void (*fatal_fn)(void *file_handle));
|
void CLG_backtrace_fn_set(void (*fatal_fn)(void *file_handle));
|
||||||
|
|
||||||
|
@ -98,6 +98,7 @@ typedef struct CLogContext {
|
|||||||
} default_type;
|
} default_type;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
|
void (*error_fn)(void *file_handle);
|
||||||
void (*fatal_fn)(void *file_handle);
|
void (*fatal_fn)(void *file_handle);
|
||||||
void (*backtrace_fn)(void *file_handle);
|
void (*backtrace_fn)(void *file_handle);
|
||||||
} callbacks;
|
} callbacks;
|
||||||
@ -352,6 +353,13 @@ static CLG_LogType *clg_ctx_type_register(CLogContext *ctx, const char *identifi
|
|||||||
return ty;
|
return ty;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void clg_ctx_error_action(CLogContext *ctx)
|
||||||
|
{
|
||||||
|
if (ctx->callbacks.error_fn != NULL) {
|
||||||
|
ctx->callbacks.error_fn(ctx->output_file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void clg_ctx_fatal_action(CLogContext *ctx)
|
static void clg_ctx_fatal_action(CLogContext *ctx)
|
||||||
{
|
{
|
||||||
if (ctx->callbacks.fatal_fn != NULL) {
|
if (ctx->callbacks.fatal_fn != NULL) {
|
||||||
@ -522,6 +530,10 @@ void CLG_logf(CLG_LogType *lg,
|
|||||||
clg_ctx_backtrace(lg->ctx);
|
clg_ctx_backtrace(lg->ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (severity == CLG_SEVERITY_ERROR) {
|
||||||
|
clg_ctx_error_action(lg->ctx);
|
||||||
|
}
|
||||||
|
|
||||||
if (severity == CLG_SEVERITY_FATAL) {
|
if (severity == CLG_SEVERITY_FATAL) {
|
||||||
clg_ctx_fatal_action(lg->ctx);
|
clg_ctx_fatal_action(lg->ctx);
|
||||||
}
|
}
|
||||||
@ -555,6 +567,12 @@ static void CLG_ctx_output_use_timestamp_set(CLogContext *ctx, int value)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Action on error severity. */
|
||||||
|
static void CLT_ctx_error_fn_set(CLogContext *ctx, void (*error_fn)(void *file_handle))
|
||||||
|
{
|
||||||
|
ctx->callbacks.error_fn = error_fn;
|
||||||
|
}
|
||||||
|
|
||||||
/** Action on fatal severity. */
|
/** Action on fatal severity. */
|
||||||
static void CLG_ctx_fatal_fn_set(CLogContext *ctx, void (*fatal_fn)(void *file_handle))
|
static void CLG_ctx_fatal_fn_set(CLogContext *ctx, void (*fatal_fn)(void *file_handle))
|
||||||
{
|
{
|
||||||
@ -674,6 +692,11 @@ void CLG_output_use_timestamp_set(int value)
|
|||||||
CLG_ctx_output_use_timestamp_set(g_ctx, value);
|
CLG_ctx_output_use_timestamp_set(g_ctx, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CLG_error_fn_set(void (*error_fn)(void *file_handle))
|
||||||
|
{
|
||||||
|
CLT_ctx_error_fn_set(g_ctx, error_fn);
|
||||||
|
}
|
||||||
|
|
||||||
void CLG_fatal_fn_set(void (*fatal_fn)(void *file_handle))
|
void CLG_fatal_fn_set(void (*fatal_fn)(void *file_handle))
|
||||||
{
|
{
|
||||||
CLG_ctx_fatal_fn_set(g_ctx, fatal_fn);
|
CLG_ctx_fatal_fn_set(g_ctx, fatal_fn);
|
||||||
|
@ -215,6 +215,11 @@ extern const char *(*MEM_name_ptr)(void *vmemh);
|
|||||||
* about memory leaks will be printed on exit. */
|
* about memory leaks will be printed on exit. */
|
||||||
void MEM_init_memleak_detection(void);
|
void MEM_init_memleak_detection(void);
|
||||||
|
|
||||||
|
/** When this has been called and memory leaks have been detected, the process will have an exit
|
||||||
|
* code that indicates failure. This can be used for when checking for memory leaks with automated
|
||||||
|
* tests. */
|
||||||
|
void MEM_enable_fail_on_memleak(void);
|
||||||
|
|
||||||
/* Switch allocator to slower but fully guarded mode. */
|
/* Switch allocator to slower but fully guarded mode. */
|
||||||
void MEM_use_guarded_allocator(void);
|
void MEM_use_guarded_allocator(void);
|
||||||
|
|
||||||
|
@ -18,6 +18,8 @@
|
|||||||
* \ingroup MEM
|
* \ingroup MEM
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <cstdlib>
|
||||||
|
|
||||||
#include "MEM_guardedalloc.h"
|
#include "MEM_guardedalloc.h"
|
||||||
#include "mallocn_intern.h"
|
#include "mallocn_intern.h"
|
||||||
|
|
||||||
@ -28,6 +30,9 @@ char free_after_leak_detection_message[] =
|
|||||||
"error, use the 'construct on first use' idiom.";
|
"error, use the 'construct on first use' idiom.";
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
static bool fail_on_memleak = false;
|
||||||
|
|
||||||
class MemLeakPrinter {
|
class MemLeakPrinter {
|
||||||
public:
|
public:
|
||||||
~MemLeakPrinter()
|
~MemLeakPrinter()
|
||||||
@ -42,6 +47,15 @@ class MemLeakPrinter {
|
|||||||
leaked_blocks,
|
leaked_blocks,
|
||||||
(double)mem_in_use / 1024 / 1024);
|
(double)mem_in_use / 1024 / 1024);
|
||||||
MEM_printmemlist();
|
MEM_printmemlist();
|
||||||
|
|
||||||
|
if (fail_on_memleak) {
|
||||||
|
/* There are many other ways to change the exit code to failure here:
|
||||||
|
* - Make the destructor noexcept(false) and throw an exception.
|
||||||
|
* - Call exit(EXIT_FAILURE).
|
||||||
|
* - Call terminate().
|
||||||
|
*/
|
||||||
|
abort();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
} // namespace
|
} // namespace
|
||||||
@ -59,3 +73,8 @@ void MEM_init_memleak_detection(void)
|
|||||||
*/
|
*/
|
||||||
static MemLeakPrinter printer;
|
static MemLeakPrinter printer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MEM_enable_fail_on_memleak(void)
|
||||||
|
{
|
||||||
|
fail_on_memleak = true;
|
||||||
|
}
|
||||||
|
@ -751,6 +751,25 @@ static int arg_handle_abort_handler_disable(int UNUSED(argc),
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void clog_abort_on_error_callback(void *fp)
|
||||||
|
{
|
||||||
|
BLI_system_backtrace(fp);
|
||||||
|
fflush(fp);
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char arg_handle_debug_exit_on_error_doc[] =
|
||||||
|
"\n\t"
|
||||||
|
"Immediately exit when internal errors are detected.";
|
||||||
|
static int arg_handle_debug_exit_on_error(int UNUSED(argc),
|
||||||
|
const char **UNUSED(argv),
|
||||||
|
void *UNUSED(data))
|
||||||
|
{
|
||||||
|
MEM_enable_fail_on_memleak();
|
||||||
|
CLG_error_fn_set(clog_abort_on_error_callback);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static const char arg_handle_background_mode_set_doc[] =
|
static const char arg_handle_background_mode_set_doc[] =
|
||||||
"\n\t"
|
"\n\t"
|
||||||
"Run in background (often used for UI-less rendering).";
|
"Run in background (often used for UI-less rendering).";
|
||||||
@ -2214,6 +2233,7 @@ void main_args_setup(bContext *C, bArgs *ba)
|
|||||||
"--debug-gpu-force-workarounds",
|
"--debug-gpu-force-workarounds",
|
||||||
CB_EX(arg_handle_debug_mode_generic_set, gpumem),
|
CB_EX(arg_handle_debug_mode_generic_set, gpumem),
|
||||||
(void *)G_DEBUG_GPU_FORCE_WORKAROUNDS);
|
(void *)G_DEBUG_GPU_FORCE_WORKAROUNDS);
|
||||||
|
BLI_argsAdd(ba, 1, NULL, "--debug-exit-on-error", CB(arg_handle_debug_exit_on_error), NULL);
|
||||||
|
|
||||||
BLI_argsAdd(ba, 1, NULL, "--verbose", CB(arg_handle_verbosity_set), NULL);
|
BLI_argsAdd(ba, 1, NULL, "--verbose", CB(arg_handle_verbosity_set), NULL);
|
||||||
|
|
||||||
|
@ -44,7 +44,7 @@ unset(_default_test_python_exe)
|
|||||||
|
|
||||||
# Standard Blender arguments for running tests.
|
# Standard Blender arguments for running tests.
|
||||||
# Specify exit code so that if a Python script error happens, the test fails.
|
# Specify exit code so that if a Python script error happens, the test fails.
|
||||||
set(TEST_BLENDER_EXE_PARAMS --background -noaudio --factory-startup --python-exit-code 1)
|
set(TEST_BLENDER_EXE_PARAMS --background -noaudio --factory-startup --debug-memory --debug-exit-on-error --python-exit-code 1)
|
||||||
|
|
||||||
# Python CTests
|
# Python CTests
|
||||||
if(WITH_BLENDER AND WITH_PYTHON)
|
if(WITH_BLENDER AND WITH_PYTHON)
|
||||||
|
@ -50,6 +50,7 @@ int main(int argc, char **argv)
|
|||||||
{
|
{
|
||||||
MEM_use_guarded_allocator();
|
MEM_use_guarded_allocator();
|
||||||
MEM_init_memleak_detection();
|
MEM_init_memleak_detection();
|
||||||
|
MEM_enable_fail_on_memleak();
|
||||||
testing::InitGoogleTest(&argc, argv);
|
testing::InitGoogleTest(&argc, argv);
|
||||||
BLENDER_GFLAGS_NAMESPACE::ParseCommandLineFlags(&argc, &argv, true);
|
BLENDER_GFLAGS_NAMESPACE::ParseCommandLineFlags(&argc, &argv, true);
|
||||||
google::InitGoogleLogging(argv[0]);
|
google::InitGoogleLogging(argv[0]);
|
||||||
|
@ -36,12 +36,12 @@ execute_process(COMMAND ${CMAKE_COMMAND} -E make_directory ${TEST_OUT_DIR})
|
|||||||
# all calls to blender use this
|
# all calls to blender use this
|
||||||
if(APPLE)
|
if(APPLE)
|
||||||
if(${CMAKE_GENERATOR} MATCHES "Xcode")
|
if(${CMAKE_GENERATOR} MATCHES "Xcode")
|
||||||
set(TEST_BLENDER_EXE_PARAMS --background -noaudio --factory-startup)
|
set(TEST_BLENDER_EXE_PARAMS --background -noaudio --factory-startup --debug-memory --debug-exit-on-error)
|
||||||
else()
|
else()
|
||||||
set(TEST_BLENDER_EXE_PARAMS --background -noaudio --factory-startup --env-system-scripts ${CMAKE_SOURCE_DIR}/release/scripts)
|
set(TEST_BLENDER_EXE_PARAMS --background -noaudio --factory-startup --debug-memory --debug-exit-on-error --env-system-scripts ${CMAKE_SOURCE_DIR}/release/scripts)
|
||||||
endif()
|
endif()
|
||||||
else()
|
else()
|
||||||
set(TEST_BLENDER_EXE_PARAMS --background -noaudio --factory-startup --env-system-scripts ${CMAKE_SOURCE_DIR}/release/scripts)
|
set(TEST_BLENDER_EXE_PARAMS --background -noaudio --factory-startup --debug-memory --debug-exit-on-error --env-system-scripts ${CMAKE_SOURCE_DIR}/release/scripts)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# for testing with valgrind prefix: valgrind --track-origins=yes --error-limit=no
|
# for testing with valgrind prefix: valgrind --track-origins=yes --error-limit=no
|
||||||
|
@ -20,6 +20,8 @@ def get_arguments(filepath, output_filepath):
|
|||||||
"-noaudio",
|
"-noaudio",
|
||||||
"--factory-startup",
|
"--factory-startup",
|
||||||
"--enable-autoexec",
|
"--enable-autoexec",
|
||||||
|
"--debug-memory",
|
||||||
|
"--debug-exit-on-error",
|
||||||
filepath,
|
filepath,
|
||||||
"-E", "CYCLES",
|
"-E", "CYCLES",
|
||||||
"-o", output_filepath,
|
"-o", output_filepath,
|
||||||
|
@ -103,6 +103,8 @@ def get_arguments(filepath, output_filepath):
|
|||||||
"-noaudio",
|
"-noaudio",
|
||||||
"--factory-startup",
|
"--factory-startup",
|
||||||
"--enable-autoexec",
|
"--enable-autoexec",
|
||||||
|
"--debug-memory",
|
||||||
|
"--debug-exit-on-error",
|
||||||
filepath,
|
filepath,
|
||||||
"-E", "BLENDER_EEVEE",
|
"-E", "BLENDER_EEVEE",
|
||||||
"-P",
|
"-P",
|
||||||
|
@ -448,16 +448,17 @@ class Report:
|
|||||||
crash = False
|
crash = False
|
||||||
output = None
|
output = None
|
||||||
try:
|
try:
|
||||||
output = subprocess.check_output(command)
|
completed_process = subprocess.run(command, stdout=subprocess.PIPE)
|
||||||
except subprocess.CalledProcessError as e:
|
if completed_process.returncode != 0:
|
||||||
crash = True
|
crash = True
|
||||||
|
output = completed_process.stdout
|
||||||
except BaseException as e:
|
except BaseException as e:
|
||||||
crash = True
|
crash = True
|
||||||
|
|
||||||
if verbose:
|
if verbose:
|
||||||
print(" ".join(command))
|
print(" ".join(command))
|
||||||
if output:
|
if (verbose or crash) and output:
|
||||||
print(output.decode("utf-8"))
|
print(output.decode("utf-8"))
|
||||||
|
|
||||||
# Detect missing filepaths and consider those errors
|
# Detect missing filepaths and consider those errors
|
||||||
for filepath, output_filepath in zip(remaining_filepaths[:], output_filepaths):
|
for filepath, output_filepath in zip(remaining_filepaths[:], output_filepaths):
|
||||||
|
@ -79,6 +79,8 @@ class AbstractBlenderRunnerTest(unittest.TestCase):
|
|||||||
'-noaudio',
|
'-noaudio',
|
||||||
'--factory-startup',
|
'--factory-startup',
|
||||||
'--enable-autoexec',
|
'--enable-autoexec',
|
||||||
|
'--debug-memory',
|
||||||
|
'--debug-exit-on-error',
|
||||||
]
|
]
|
||||||
|
|
||||||
if blendfile:
|
if blendfile:
|
||||||
|
@ -39,6 +39,8 @@ def get_arguments(filepath, output_filepath):
|
|||||||
"-noaudio",
|
"-noaudio",
|
||||||
"--factory-startup",
|
"--factory-startup",
|
||||||
"--enable-autoexec",
|
"--enable-autoexec",
|
||||||
|
"--debug-memory",
|
||||||
|
"--debug-exit-on-error",
|
||||||
filepath,
|
filepath,
|
||||||
"-P",
|
"-P",
|
||||||
os.path.realpath(__file__),
|
os.path.realpath(__file__),
|
||||||
|
@ -30,7 +30,7 @@ execute_process(COMMAND ${CMAKE_COMMAND} -E make_directory ${TEST_OUT_DIR})
|
|||||||
# endif()
|
# endif()
|
||||||
|
|
||||||
# for testing with valgrind prefix: valgrind --track-origins=yes --error-limit=no
|
# for testing with valgrind prefix: valgrind --track-origins=yes --error-limit=no
|
||||||
set(TEST_BLENDER_EXE $<TARGET_FILE:blender> --background -noaudio --factory-startup --env-system-scripts ${CMAKE_SOURCE_DIR}/release/scripts)
|
set(TEST_BLENDER_EXE $<TARGET_FILE:blender> --background -noaudio --factory-startup --debug-memory --debug-exit-on-error --env-system-scripts ${CMAKE_SOURCE_DIR}/release/scripts)
|
||||||
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
@ -39,6 +39,8 @@ def get_arguments(filepath, output_filepath):
|
|||||||
"-noaudio",
|
"-noaudio",
|
||||||
"--factory-startup",
|
"--factory-startup",
|
||||||
"--enable-autoexec",
|
"--enable-autoexec",
|
||||||
|
"--debug-memory",
|
||||||
|
"--debug-exit-on-error",
|
||||||
filepath,
|
filepath,
|
||||||
"-E", "BLENDER_WORKBENCH",
|
"-E", "BLENDER_WORKBENCH",
|
||||||
"-P",
|
"-P",
|
||||||
|
Loading…
Reference in New Issue
Block a user