mirror of
https://github.com/Ed94/raddebugger.git
synced 2026-08-09 07:18:09 +00:00
promote test-definition concepts from torture to base layer; reorganize tests to be nested in their respective layer; eliminate 't_group', just use containing layer
This commit is contained in:
+1
-1
@@ -49,7 +49,7 @@ commands =
|
|||||||
// .f1 = { .win = "raddbg_stable --ipc kill_all && build raddbg", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, },
|
// .f1 = { .win = "raddbg_stable --ipc kill_all && build raddbg", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, },
|
||||||
// .f1 = { .win = "raddbg_stable --ipc kill_all && build raddbg debug telemetry", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, },
|
// .f1 = { .win = "raddbg_stable --ipc kill_all && build raddbg debug telemetry", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, },
|
||||||
// .f1 = { .win = "raddbg_stable --ipc kill_all && build radbin", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, },
|
// .f1 = { .win = "raddbg_stable --ipc kill_all && build radbin", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, },
|
||||||
.f1 = { .win = "raddbg_stable --ipc kill_all && build raddbg debug telemetry", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, },
|
.f1 = { .win = "raddbg_stable --ipc kill_all && build torture", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, },
|
||||||
|
|
||||||
//- rjf: [raddbg wsl]
|
//- rjf: [raddbg wsl]
|
||||||
// .f1 = { .win = "wsl ./build.sh raddbg", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, },
|
// .f1 = { .win = "wsl ./build.sh raddbg", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, },
|
||||||
|
|||||||
+6
-1
@@ -6,7 +6,6 @@ target:
|
|||||||
executable: "build/raddbg.exe"
|
executable: "build/raddbg.exe"
|
||||||
working_directory: build
|
working_directory: build
|
||||||
arguments: "--user:raddbg_test.user --project:raddbg_test.project"
|
arguments: "--user:raddbg_test.user --project:raddbg_test.project"
|
||||||
enabled: 1
|
|
||||||
debug_subprocesses: 0
|
debug_subprocesses: 0
|
||||||
}
|
}
|
||||||
target:
|
target:
|
||||||
@@ -38,3 +37,9 @@ target:
|
|||||||
working_directory: build
|
working_directory: build
|
||||||
arguments: "--rdi raddbg.pdb"
|
arguments: "--rdi raddbg.pdb"
|
||||||
}
|
}
|
||||||
|
target:
|
||||||
|
{
|
||||||
|
executable: "build/torture.exe"
|
||||||
|
working_directory: "build/"
|
||||||
|
enabled: 1
|
||||||
|
}
|
||||||
|
|||||||
@@ -138,6 +138,10 @@
|
|||||||
# define BUILD_DEBUG 1
|
# define BUILD_DEBUG 1
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if !defined(BUILD_TESTS)
|
||||||
|
# define BUILD_TESTS 0
|
||||||
|
#endif
|
||||||
|
|
||||||
#if !defined(BUILD_SUPPLEMENTARY_UNIT)
|
#if !defined(BUILD_SUPPLEMENTARY_UNIT)
|
||||||
# define BUILD_SUPPLEMENTARY_UNIT 0
|
# define BUILD_SUPPLEMENTARY_UNIT 0
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -670,6 +670,16 @@ Compiler;
|
|||||||
# define Compiler_CURRENT Compiler_Null
|
# define Compiler_CURRENT Compiler_Null
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
typedef enum Linker
|
||||||
|
{
|
||||||
|
Linker_Null,
|
||||||
|
Linker_radlink,
|
||||||
|
Linker_msvc,
|
||||||
|
Linker_lld,
|
||||||
|
Linker_COUNT
|
||||||
|
}
|
||||||
|
Linker;
|
||||||
|
|
||||||
////////////////////////////////
|
////////////////////////////////
|
||||||
//~ rjf: Access Flags
|
//~ rjf: Access Flags
|
||||||
|
|
||||||
@@ -1076,6 +1086,11 @@ internal DateTime date_time_from_dense_time(DenseTime time);
|
|||||||
internal DateTime date_time_from_micro_seconds(U64 time);
|
internal DateTime date_time_from_micro_seconds(U64 time);
|
||||||
internal DateTime date_time_from_unix_time(U64 unix_time);
|
internal DateTime date_time_from_unix_time(U64 unix_time);
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
//~ rjf: @per_os_impl Debugger Attachment Checking
|
||||||
|
|
||||||
|
internal B32 debugger_is_attached(void);
|
||||||
|
|
||||||
////////////////////////////////
|
////////////////////////////////
|
||||||
//~ rjf: @per_os_impl Platform Time Functions
|
//~ rjf: @per_os_impl Platform Time Functions
|
||||||
|
|
||||||
|
|||||||
@@ -25,6 +25,7 @@
|
|||||||
#include "base_markup.c"
|
#include "base_markup.c"
|
||||||
#include "base_meta.c"
|
#include "base_meta.c"
|
||||||
#include "base_log.c"
|
#include "base_log.c"
|
||||||
|
#include "base_test.c"
|
||||||
#include "base_entry_point.c"
|
#include "base_entry_point.c"
|
||||||
|
|
||||||
#if OS_WINDOWS
|
#if OS_WINDOWS
|
||||||
|
|||||||
@@ -27,6 +27,7 @@
|
|||||||
#include "base_markup.h"
|
#include "base_markup.h"
|
||||||
#include "base_meta.h"
|
#include "base_meta.h"
|
||||||
#include "base_log.h"
|
#include "base_log.h"
|
||||||
|
#include "base_test.h"
|
||||||
#include "base_entry_point.h"
|
#include "base_entry_point.h"
|
||||||
|
|
||||||
#if OS_WINDOWS
|
#if OS_WINDOWS
|
||||||
|
|||||||
@@ -423,6 +423,10 @@ str8_find_needle(String8 string, U64 start_pos, String8 needle, StringMatchFlags
|
|||||||
{
|
{
|
||||||
needle_first_char_adjusted = upper_from_char(needle_first_char_adjusted);
|
needle_first_char_adjusted = upper_from_char(needle_first_char_adjusted);
|
||||||
}
|
}
|
||||||
|
if(adjusted_flags & StringMatchFlag_SlashInsensitive)
|
||||||
|
{
|
||||||
|
needle_first_char_adjusted = correct_slash_from_char(needle_first_char_adjusted);
|
||||||
|
}
|
||||||
for(;p < stop_p; p += 1)
|
for(;p < stop_p; p += 1)
|
||||||
{
|
{
|
||||||
U8 haystack_char_adjusted = *p;
|
U8 haystack_char_adjusted = *p;
|
||||||
@@ -430,6 +434,10 @@ str8_find_needle(String8 string, U64 start_pos, String8 needle, StringMatchFlags
|
|||||||
{
|
{
|
||||||
haystack_char_adjusted = upper_from_char(haystack_char_adjusted);
|
haystack_char_adjusted = upper_from_char(haystack_char_adjusted);
|
||||||
}
|
}
|
||||||
|
if(adjusted_flags & StringMatchFlag_SlashInsensitive)
|
||||||
|
{
|
||||||
|
haystack_char_adjusted = correct_slash_from_char(haystack_char_adjusted);
|
||||||
|
}
|
||||||
if(haystack_char_adjusted == needle_first_char_adjusted)
|
if(haystack_char_adjusted == needle_first_char_adjusted)
|
||||||
{
|
{
|
||||||
if(str8_match(str8_range(p + 1, string_opl), needle_tail, adjusted_flags))
|
if(str8_match(str8_range(p + 1, string_opl), needle_tail, adjusted_flags))
|
||||||
|
|||||||
@@ -0,0 +1,2 @@
|
|||||||
|
// Copyright (c) Epic Games Tools
|
||||||
|
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
// Copyright (c) Epic Games Tools
|
||||||
|
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||||
|
|
||||||
|
#ifndef BASE_TEST_H
|
||||||
|
#define BASE_TEST_H
|
||||||
|
|
||||||
|
typedef enum TestStatus
|
||||||
|
{
|
||||||
|
TestStatus_Fail,
|
||||||
|
TestStatus_Crash,
|
||||||
|
TestStatus_Pass,
|
||||||
|
TestStatus_Skip,
|
||||||
|
TestStatus_COUNT
|
||||||
|
}
|
||||||
|
TestStatus;
|
||||||
|
|
||||||
|
typedef struct TestResult TestResult;
|
||||||
|
struct TestResult
|
||||||
|
{
|
||||||
|
TestStatus status;
|
||||||
|
char *fail_file;
|
||||||
|
int fail_line;
|
||||||
|
char *fail_cond;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define TEST_FUNCTION_SIG(name) void name(Arena *arena, String8 user_data, TestResult *result_out, String8List *test_out)
|
||||||
|
#define TEST_FUNCTION_DEF(name) TEST_FUNCTION_SIG(test__##name)
|
||||||
|
typedef TEST_FUNCTION_SIG(TestFunctionType);
|
||||||
|
|
||||||
|
typedef struct TestInfo TestInfo;
|
||||||
|
struct TestInfo
|
||||||
|
{
|
||||||
|
String8 layer;
|
||||||
|
String8 label;
|
||||||
|
int decl_line;
|
||||||
|
B32 skip;
|
||||||
|
TestFunctionType *test_fn;
|
||||||
|
};
|
||||||
|
|
||||||
|
#if BUILD_TESTS
|
||||||
|
global U64 test_infos_count = 0;
|
||||||
|
global TestInfo test_infos[0xffff] = {0};
|
||||||
|
#define AddTest(name, file_name, line, skip_, ...) \
|
||||||
|
TEST_FUNCTION_DEF(name);\
|
||||||
|
__VA_ARGS__ void add_test__##name(void)\
|
||||||
|
{\
|
||||||
|
String8 file = str8_lit(file_name);\
|
||||||
|
U64 src_pos = str8_find_needle(file, 0, s("src/"), StringMatchFlag_SlashInsensitive);\
|
||||||
|
String8 layer_folder = str8_skip(file, src_pos+4);\
|
||||||
|
U64 layer_slash_pos = str8_find_needle(layer_folder, 0, s("/"), StringMatchFlag_SlashInsensitive);\
|
||||||
|
String8 layer_name = str8_prefix(layer_folder, layer_slash_pos);\
|
||||||
|
test_infos[test_infos_count].layer = layer_name;\
|
||||||
|
test_infos[test_infos_count].label = str8_lit(#name);\
|
||||||
|
test_infos[test_infos_count].decl_line = (line);\
|
||||||
|
test_infos[test_infos_count].skip = (skip_);\
|
||||||
|
test_infos[test_infos_count].test_fn = test__##name;\
|
||||||
|
test_infos_count += 1;\
|
||||||
|
}
|
||||||
|
# if COMPILER_MSVC
|
||||||
|
# pragma section(".CRT$XCU", read)
|
||||||
|
# define DeclareTest(name, skip) \
|
||||||
|
AddTest(name, __FILE__, __LINE__, (skip))\
|
||||||
|
__declspec(allocate(".CRT$XCU")) void (*add_test_ptr__##name)(void) = add_test__##name;\
|
||||||
|
__pragma(comment(linker, "/include:" Stringify(add_test_ptr__##name)))
|
||||||
|
# elif COMPILER_GCC || COMPILER_CLANG
|
||||||
|
# define DeclareTest(name, skip) AddTest(name, __FILE__, __LINE__, (skip), __attribute__((constructor)))
|
||||||
|
# else
|
||||||
|
# error DeclareTest not defined for this compiler.
|
||||||
|
# endif
|
||||||
|
#else
|
||||||
|
# define DeclareTest(name, skip)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define Test(name) DeclareTest(name, 0)\
|
||||||
|
TEST_FUNCTION_DEF(name)
|
||||||
|
#define SkippedTest(name) DeclareTest(name, 1)\
|
||||||
|
TEST_FUNCTION_DEF(name)
|
||||||
|
|
||||||
|
#define TestCheck(c) do { if (!(c)) {\
|
||||||
|
*result_out = (TestResult){ .fail_file = __FILE__, .fail_line = __LINE__, .fail_cond = Stringify(c) };\
|
||||||
|
if(debugger_is_attached()) { Trap(); }\
|
||||||
|
return;\
|
||||||
|
} } while(0)
|
||||||
|
|
||||||
|
#endif // BASE_TEST_H
|
||||||
@@ -4,7 +4,7 @@
|
|||||||
TEST(str8_list_substr)
|
TEST(str8_list_substr)
|
||||||
{
|
{
|
||||||
String8List zero_list = {0};
|
String8List zero_list = {0};
|
||||||
|
|
||||||
{
|
{
|
||||||
String8List list = {0};
|
String8List list = {0};
|
||||||
str8_list_pushf(arena, &list, "a");
|
str8_list_pushf(arena, &list, "a");
|
||||||
@@ -14,7 +14,7 @@ TEST(str8_list_substr)
|
|||||||
String8 result = str8_list_join(arena, &list, 0);
|
String8 result = str8_list_join(arena, &list, 0);
|
||||||
T_Ok(str8_match(result, str8_lit("abc"), 0));
|
T_Ok(str8_match(result, str8_lit("abc"), 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
String8List list = {0};
|
String8List list = {0};
|
||||||
str8_list_pushf(arena, &list, "a");
|
str8_list_pushf(arena, &list, "a");
|
||||||
@@ -24,9 +24,9 @@ TEST(str8_list_substr)
|
|||||||
String8 result = str8_list_join(arena, &list, 0);
|
String8 result = str8_list_join(arena, &list, 0);
|
||||||
T_Ok(str8_match(result, str8_lit("abc"), 0));
|
T_Ok(str8_match(result, str8_lit("abc"), 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|
||||||
String8List list = {0};
|
String8List list = {0};
|
||||||
str8_list_pushf(arena, &list, "a");
|
str8_list_pushf(arena, &list, "a");
|
||||||
str8_list_pushf(arena, &list, "bcd");
|
str8_list_pushf(arena, &list, "bcd");
|
||||||
@@ -34,9 +34,9 @@ TEST(str8_list_substr)
|
|||||||
String8 result = str8_list_join(arena, &sub, 0);
|
String8 result = str8_list_join(arena, &sub, 0);
|
||||||
T_Ok(str8_match(result, str8_lit("c"), 0));
|
T_Ok(str8_match(result, str8_lit("c"), 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|
||||||
String8List list = {0};
|
String8List list = {0};
|
||||||
str8_list_pushf(arena, &list, "a");
|
str8_list_pushf(arena, &list, "a");
|
||||||
str8_list_pushf(arena, &list, "bcd");
|
str8_list_pushf(arena, &list, "bcd");
|
||||||
@@ -44,7 +44,7 @@ TEST(str8_list_substr)
|
|||||||
String8 result = str8_list_join(arena, &sub, 0);
|
String8 result = str8_list_join(arena, &sub, 0);
|
||||||
T_Ok(str8_match(result, str8_lit("b"), 0));
|
T_Ok(str8_match(result, str8_lit("b"), 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
String8List list = {0};
|
String8List list = {0};
|
||||||
str8_list_pushf(arena, &list, "ab");
|
str8_list_pushf(arena, &list, "ab");
|
||||||
@@ -54,14 +54,14 @@ TEST(str8_list_substr)
|
|||||||
String8 result = str8_list_join(arena, &sub, 0);
|
String8 result = str8_list_join(arena, &sub, 0);
|
||||||
T_Ok(str8_match(result, str8_lit("bcde"), 0));
|
T_Ok(str8_match(result, str8_lit("bcde"), 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
String8List list = {0};
|
String8List list = {0};
|
||||||
str8_list_pushf(arena, &list, "abc");
|
str8_list_pushf(arena, &list, "abc");
|
||||||
|
|
||||||
String8List zero = str8_list_substr(arena, list, r1u64(0, 0));
|
String8List zero = str8_list_substr(arena, list, r1u64(0, 0));
|
||||||
T_Ok(MemoryMatchStruct(&zero, &zero_list));
|
T_Ok(MemoryMatchStruct(&zero, &zero_list));
|
||||||
|
|
||||||
String8List out_of_bounds_range = str8_list_substr(arena, list, r1u64(max_U64/2, max_U64));
|
String8List out_of_bounds_range = str8_list_substr(arena, list, r1u64(max_U64/2, max_U64));
|
||||||
T_Ok(MemoryMatchStruct(&out_of_bounds_range, &zero_list));
|
T_Ok(MemoryMatchStruct(&out_of_bounds_range, &zero_list));
|
||||||
}
|
}
|
||||||
@@ -84,7 +84,7 @@ TEST(bit_array)
|
|||||||
T_Ok(r == expected_r);
|
T_Ok(r == expected_r);
|
||||||
if (r) {
|
if (r) {
|
||||||
T_Ok(idx == expected_idx);
|
T_Ok(idx == expected_idx);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -112,11 +112,11 @@ TEST(match_wildcard)
|
|||||||
T_Ok(str8_match_wildcard(str8_lit(""), str8_lit("*?"), 0) == 0);
|
T_Ok(str8_match_wildcard(str8_lit(""), str8_lit("*?"), 0) == 0);
|
||||||
T_Ok(str8_match_wildcard(str8_lit(""), str8_lit("?*"), 0) == 0);
|
T_Ok(str8_match_wildcard(str8_lit(""), str8_lit("?*"), 0) == 0);
|
||||||
T_Ok(str8_match_wildcard(str8_lit("a"), str8_lit(""), 0) == 0);
|
T_Ok(str8_match_wildcard(str8_lit("a"), str8_lit(""), 0) == 0);
|
||||||
|
|
||||||
// exact
|
// exact
|
||||||
T_Ok(str8_match_wildcard(str8_lit("a"), str8_lit("a"), 0) == 1);
|
T_Ok(str8_match_wildcard(str8_lit("a"), str8_lit("a"), 0) == 1);
|
||||||
T_Ok(str8_match_wildcard(str8_lit("a"), str8_lit("A"), 0) == 0);
|
T_Ok(str8_match_wildcard(str8_lit("a"), str8_lit("A"), 0) == 0);
|
||||||
|
|
||||||
// ?
|
// ?
|
||||||
T_Ok(str8_match_wildcard(str8_lit("a"), str8_lit("?"), 0) == 1);
|
T_Ok(str8_match_wildcard(str8_lit("a"), str8_lit("?"), 0) == 1);
|
||||||
T_Ok(str8_match_wildcard(str8_lit(""), str8_lit("?"), 0) == 0);
|
T_Ok(str8_match_wildcard(str8_lit(""), str8_lit("?"), 0) == 0);
|
||||||
@@ -126,7 +126,7 @@ TEST(match_wildcard)
|
|||||||
T_Ok(str8_match_wildcard(str8_lit("ab"), str8_lit("??"), 0) == 1);
|
T_Ok(str8_match_wildcard(str8_lit("ab"), str8_lit("??"), 0) == 1);
|
||||||
T_Ok(str8_match_wildcard(str8_lit("abc"), str8_lit("a?c"), 0) == 1);
|
T_Ok(str8_match_wildcard(str8_lit("abc"), str8_lit("a?c"), 0) == 1);
|
||||||
T_Ok(str8_match_wildcard(str8_lit("ab"), str8_lit("???"), 0) == 0);
|
T_Ok(str8_match_wildcard(str8_lit("ab"), str8_lit("???"), 0) == 0);
|
||||||
|
|
||||||
// *
|
// *
|
||||||
T_Ok(str8_match_wildcard(str8_lit(""), str8_lit("*"), 0) == 1);
|
T_Ok(str8_match_wildcard(str8_lit(""), str8_lit("*"), 0) == 1);
|
||||||
T_Ok(str8_match_wildcard(str8_lit("a"), str8_lit("*"), 0) == 1);
|
T_Ok(str8_match_wildcard(str8_lit("a"), str8_lit("*"), 0) == 1);
|
||||||
@@ -140,32 +140,32 @@ TEST(match_wildcard)
|
|||||||
T_Ok(str8_match_wildcard(str8_lit("abc"), str8_lit("a**c"), 0) == 1);
|
T_Ok(str8_match_wildcard(str8_lit("abc"), str8_lit("a**c"), 0) == 1);
|
||||||
T_Ok(str8_match_wildcard(str8_lit("abc"), str8_lit("a*b*c"), 0) == 1);
|
T_Ok(str8_match_wildcard(str8_lit("abc"), str8_lit("a*b*c"), 0) == 1);
|
||||||
T_Ok(str8_match_wildcard(str8_lit("abc"), str8_lit("*a*d*"), 0) == 0);
|
T_Ok(str8_match_wildcard(str8_lit("abc"), str8_lit("*a*d*"), 0) == 0);
|
||||||
|
|
||||||
T_Ok(str8_match_wildcard(str8_lit("abcd"), str8_lit("a*d"), 0) == 1);
|
T_Ok(str8_match_wildcard(str8_lit("abcd"), str8_lit("a*d"), 0) == 1);
|
||||||
T_Ok(str8_match_wildcard(str8_lit("abefcdgiescdfimde"), str8_lit("ab*cd?i*de"), 0) == 1);
|
T_Ok(str8_match_wildcard(str8_lit("abefcdgiescdfimde"), str8_lit("ab*cd?i*de"), 0) == 1);
|
||||||
T_Ok(str8_match_wildcard(str8_lit("mississippi"), str8_lit("m*iss*ppi"), 0) == 1);
|
T_Ok(str8_match_wildcard(str8_lit("mississippi"), str8_lit("m*iss*ppi"), 0) == 1);
|
||||||
T_Ok(str8_match_wildcard(str8_lit("abc"), str8_lit("*b"), 0) == 0);
|
T_Ok(str8_match_wildcard(str8_lit("abc"), str8_lit("*b"), 0) == 0);
|
||||||
T_Ok(str8_match_wildcard(str8_lit("a"), str8_lit("aa"), 0) == 0);
|
T_Ok(str8_match_wildcard(str8_lit("a"), str8_lit("aa"), 0) == 0);
|
||||||
T_Ok(str8_match_wildcard(str8_lit("aa"), str8_lit("a"), 0) == 0);
|
T_Ok(str8_match_wildcard(str8_lit("aa"), str8_lit("a"), 0) == 0);
|
||||||
|
|
||||||
// case insensitive
|
// case insensitive
|
||||||
T_Ok(str8_match_wildcard(str8_lit("a"), str8_lit("A"), StringMatchFlag_CaseInsensitive) == 1);
|
T_Ok(str8_match_wildcard(str8_lit("a"), str8_lit("A"), StringMatchFlag_CaseInsensitive) == 1);
|
||||||
T_Ok(str8_match_wildcard(str8_lit("FooBar"), str8_lit("foobar"), StringMatchFlag_CaseInsensitive) == 1);
|
T_Ok(str8_match_wildcard(str8_lit("FooBar"), str8_lit("foobar"), StringMatchFlag_CaseInsensitive) == 1);
|
||||||
T_Ok(str8_match_wildcard(str8_lit("Foobar"), str8_lit("foo*"), StringMatchFlag_CaseInsensitive) == 1);
|
T_Ok(str8_match_wildcard(str8_lit("Foobar"), str8_lit("foo*"), StringMatchFlag_CaseInsensitive) == 1);
|
||||||
|
|
||||||
// right side sloppy
|
// right side sloppy
|
||||||
T_Ok(str8_match_wildcard(str8_lit("abc"), str8_lit("ab"), StringMatchFlag_RightSideSloppy) == 1);
|
T_Ok(str8_match_wildcard(str8_lit("abc"), str8_lit("ab"), StringMatchFlag_RightSideSloppy) == 1);
|
||||||
T_Ok(str8_match_wildcard(str8_lit("abc"), str8_lit(""), StringMatchFlag_RightSideSloppy) == 1);
|
T_Ok(str8_match_wildcard(str8_lit("abc"), str8_lit(""), StringMatchFlag_RightSideSloppy) == 1);
|
||||||
T_Ok(str8_match_wildcard(str8_lit(""), str8_lit("a"), StringMatchFlag_RightSideSloppy) == 0);
|
T_Ok(str8_match_wildcard(str8_lit(""), str8_lit("a"), StringMatchFlag_RightSideSloppy) == 0);
|
||||||
|
|
||||||
// slash insensitive
|
// slash insensitive
|
||||||
T_Ok(str8_match_wildcard(str8_lit("a/b"), str8_lit("a\\b"), 0) == 0);
|
T_Ok(str8_match_wildcard(str8_lit("a/b"), str8_lit("a\\b"), 0) == 0);
|
||||||
T_Ok(str8_match_wildcard(str8_lit("a/b"), str8_lit("a\\b"), StringMatchFlag_SlashInsensitive) == 1);
|
T_Ok(str8_match_wildcard(str8_lit("a/b"), str8_lit("a\\b"), StringMatchFlag_SlashInsensitive) == 1);
|
||||||
T_Ok(str8_match_wildcard(str8_lit("a/b/c"), str8_lit("a\\*\\c"), StringMatchFlag_SlashInsensitive) == 1);
|
T_Ok(str8_match_wildcard(str8_lit("a/b/c"), str8_lit("a\\*\\c"), StringMatchFlag_SlashInsensitive) == 1);
|
||||||
|
|
||||||
// combined
|
// combined
|
||||||
T_Ok(str8_match_wildcard(str8_lit("Ab\\Cde"), str8_lit("ab/*e"), StringMatchFlag_CaseInsensitive|StringMatchFlag_SlashInsensitive) == 1);
|
T_Ok(str8_match_wildcard(str8_lit("Ab\\Cde"), str8_lit("ab/*e"), StringMatchFlag_CaseInsensitive|StringMatchFlag_SlashInsensitive) == 1);
|
||||||
|
|
||||||
T_Ok(str8_match_wildcard(str8_lit("abc"), str8_lit("*?*"), 0) == 1);
|
T_Ok(str8_match_wildcard(str8_lit("abc"), str8_lit("*?*"), 0) == 1);
|
||||||
T_Ok(str8_match_wildcard(str8_lit("a"), str8_lit("*?*"), 0) == 1);
|
T_Ok(str8_match_wildcard(str8_lit("a"), str8_lit("*?*"), 0) == 1);
|
||||||
T_Ok(str8_match_wildcard(str8_lit(""), str8_lit("*?*"), 0) == 0);
|
T_Ok(str8_match_wildcard(str8_lit(""), str8_lit("*?*"), 0) == 0);
|
||||||
@@ -178,17 +178,17 @@ TEST(hash_map)
|
|||||||
{
|
{
|
||||||
{
|
{
|
||||||
HashMap hm = {0};
|
HashMap hm = {0};
|
||||||
|
|
||||||
U64 test_count = 256;
|
U64 test_count = 256;
|
||||||
for EachIndex(i, test_count) { hash_map_push_u64_u64(arena, &hm, i, i*2); }
|
for EachIndex(i, test_count) { hash_map_push_u64_u64(arena, &hm, i, i*2); }
|
||||||
|
|
||||||
// test tombstone reuse
|
// test tombstone reuse
|
||||||
for (U64 i = 10; i < test_count; ++i) {
|
for (U64 i = 10; i < test_count; ++i) {
|
||||||
HashMapNode *test_node = hash_map_search(&hm, hash_map_hasher(str8_struct(&i)), (HashMapKey){ .key_u64 = i }, hash_map_match_u64);
|
HashMapNode *test_node = hash_map_search(&hm, hash_map_hasher(str8_struct(&i)), (HashMapKey){ .key_u64 = i }, hash_map_match_u64);
|
||||||
T_Ok(hash_map_purge_u64(&hm, i) == 1);
|
T_Ok(hash_map_purge_u64(&hm, i) == 1);
|
||||||
T_Ok(hash_map_push_u64_u64(arena, &hm, i, 10) == test_node);
|
T_Ok(hash_map_push_u64_u64(arena, &hm, i, 10) == test_node);
|
||||||
}
|
}
|
||||||
|
|
||||||
// delete some items and after search them
|
// delete some items and after search them
|
||||||
for (U64 i = 0; i < 10; ++i) {
|
for (U64 i = 0; i < 10; ++i) {
|
||||||
T_Ok(hash_map_purge_u64(&hm, i));
|
T_Ok(hash_map_purge_u64(&hm, i));
|
||||||
@@ -197,37 +197,37 @@ TEST(hash_map)
|
|||||||
for (U64 i = 0; i < 10; ++i) {
|
for (U64 i = 0; i < 10; ++i) {
|
||||||
T_Ok(hash_map_search_u64_raw(&hm, i) == 0);
|
T_Ok(hash_map_search_u64_raw(&hm, i) == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// extract pairs and test sorting
|
// extract pairs and test sorting
|
||||||
HashMapKeyValue *pairs = key_value_from_hash_map(arena, &hm);
|
HashMapKeyValue *pairs = key_value_from_hash_map(arena, &hm);
|
||||||
sort_hash_map_key_value_u64(pairs, hm.count);
|
sort_hash_map_key_value_u64(pairs, hm.count);
|
||||||
for (U64 i = 1; i < hm.count; ++i) {
|
for (U64 i = 1; i < hm.count; ++i) {
|
||||||
T_Ok(pairs[i-1].key.key_u64 < pairs[i].key.key_u64);
|
T_Ok(pairs[i-1].key.key_u64 < pairs[i].key.key_u64);
|
||||||
}
|
}
|
||||||
|
|
||||||
// did purge put all the nodes on free list?
|
// did purge put all the nodes on free list?
|
||||||
hash_map_purge(&hm);
|
hash_map_purge(&hm);
|
||||||
U64 free_list_count = 0;
|
U64 free_list_count = 0;
|
||||||
for (HashMapNode *n = hm.free_list; n != 0; n = n->next) { free_list_count += 1; }
|
for (HashMapNode *n = hm.free_list; n != 0; n = n->next) { free_list_count += 1; }
|
||||||
T_Ok(free_list_count == test_count);
|
T_Ok(free_list_count == test_count);
|
||||||
}
|
}
|
||||||
|
|
||||||
// test search through tombstones
|
// test search through tombstones
|
||||||
{
|
{
|
||||||
HashMap hm = {0};
|
HashMap hm = {0};
|
||||||
|
|
||||||
U64 hash = 12345;
|
U64 hash = 12345;
|
||||||
|
|
||||||
hash_map_push(arena, &hm, hash, (HashMapKeyValue){ .key = { .key_u64 = 1 }, .value = { .value_u64 = 10 } }, hash_map_match_u64);
|
hash_map_push(arena, &hm, hash, (HashMapKeyValue){ .key = { .key_u64 = 1 }, .value = { .value_u64 = 10 } }, hash_map_match_u64);
|
||||||
hash_map_push(arena, &hm, hash, (HashMapKeyValue){ .key = { .key_u64 = 2 }, .value = { .value_u64 = 20 } }, hash_map_match_u64);
|
hash_map_push(arena, &hm, hash, (HashMapKeyValue){ .key = { .key_u64 = 2 }, .value = { .value_u64 = 20 } }, hash_map_match_u64);
|
||||||
hash_map_push(arena, &hm, hash, (HashMapKeyValue){ .key = { .key_u64 = 3 }, .value = { .value_u64 = 30 } }, hash_map_match_u64);
|
hash_map_push(arena, &hm, hash, (HashMapKeyValue){ .key = { .key_u64 = 3 }, .value = { .value_u64 = 30 } }, hash_map_match_u64);
|
||||||
|
|
||||||
T_Ok(hash_map_purge_u64(&hm, 1));
|
T_Ok(hash_map_purge_u64(&hm, 1));
|
||||||
|
|
||||||
T_Ok(hash_map_search(&hm, hash, (HashMapKey){ .key_u64 = 2 }, hash_map_match_u64)->v.value.value_u64 == 20);
|
T_Ok(hash_map_search(&hm, hash, (HashMapKey){ .key_u64 = 2 }, hash_map_match_u64)->v.value.value_u64 == 20);
|
||||||
T_Ok(hash_map_search(&hm, hash, (HashMapKey){ .key_u64 = 3 }, hash_map_match_u64)->v.value.value_u64 == 30);
|
T_Ok(hash_map_search(&hm, hash, (HashMapKey){ .key_u64 = 3 }, hash_map_match_u64)->v.value.value_u64 == 30);
|
||||||
}
|
}
|
||||||
|
|
||||||
// interleaved purges
|
// interleaved purges
|
||||||
{
|
{
|
||||||
HashMap hm = {0};
|
HashMap hm = {0};
|
||||||
@@ -235,107 +235,106 @@ TEST(hash_map)
|
|||||||
for EachIndex(i, 256) { hash_map_push_u64_u64(arena, &hm, i, round); }
|
for EachIndex(i, 256) { hash_map_push_u64_u64(arena, &hm, i, round); }
|
||||||
for (U64 i = 0; i < 256; i += 2) { hash_map_purge_u64(&hm, i); }
|
for (U64 i = 0; i < 256; i += 2) { hash_map_purge_u64(&hm, i); }
|
||||||
for (U64 i = 0; i < 256; i += 2) { hash_map_push_u64_u64(arena, &hm, i, round + 1); }
|
for (U64 i = 0; i < 256; i += 2) { hash_map_push_u64_u64(arena, &hm, i, round + 1); }
|
||||||
|
|
||||||
T_Ok(hm.count == 256);
|
T_Ok(hm.count == 256);
|
||||||
T_Ok(hm.tombstone_count == 0);
|
T_Ok(hm.tombstone_count == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
for EachIndex(i, 100) {
|
for EachIndex(i, 100) {
|
||||||
T_Ok(hash_map_purge_u64(&hm, 123123123 + i) == 0);
|
T_Ok(hash_map_purge_u64(&hm, 123123123 + i) == 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// empty hash map test
|
// empty hash map test
|
||||||
{
|
{
|
||||||
HashMap hm = {0};
|
HashMap hm = {0};
|
||||||
|
|
||||||
T_Ok(hash_map_search_u64_raw(&hm, 123) == 0);
|
T_Ok(hash_map_search_u64_raw(&hm, 123) == 0);
|
||||||
T_Ok(hash_map_purge_u64(&hm, 123) == 0);
|
T_Ok(hash_map_purge_u64(&hm, 123) == 0);
|
||||||
|
|
||||||
HashMapKeyValue *pairs = key_value_from_hash_map(arena, &hm);
|
HashMapKeyValue *pairs = key_value_from_hash_map(arena, &hm);
|
||||||
T_Ok(pairs == 0 || hm.count == 0);
|
T_Ok(pairs == 0 || hm.count == 0);
|
||||||
|
|
||||||
hash_map_purge(&hm);
|
hash_map_purge(&hm);
|
||||||
T_Ok(hm.count == 0);
|
T_Ok(hm.count == 0);
|
||||||
T_Ok(hm.tombstone_count == 0);
|
T_Ok(hm.tombstone_count == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// heavy collision
|
// heavy collision
|
||||||
{
|
{
|
||||||
HashMap hm = {0};
|
HashMap hm = {0};
|
||||||
|
|
||||||
U64 hash = 12345;
|
U64 hash = 12345;
|
||||||
U64 count = 1024;
|
U64 count = 1024;
|
||||||
|
|
||||||
for EachIndex(i, count) {
|
for EachIndex(i, count) {
|
||||||
hash_map_push(arena, &hm, hash, (HashMapKeyValue){ .key = { .key_u64 = i }, .value = { .value_u64 = i*3 } }, hash_map_match_u64);
|
hash_map_push(arena, &hm, hash, (HashMapKeyValue){ .key = { .key_u64 = i }, .value = { .value_u64 = i*3 } }, hash_map_match_u64);
|
||||||
}
|
}
|
||||||
|
|
||||||
for EachIndex(i, count) {
|
for EachIndex(i, count) {
|
||||||
T_Ok(hash_map_search(&hm, hash, (HashMapKey){ .key_u64 = i }, hash_map_match_u64)->v.value.value_u64 == i*3);
|
T_Ok(hash_map_search(&hm, hash, (HashMapKey){ .key_u64 = i }, hash_map_match_u64)->v.value.value_u64 == i*3);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// duplicate key test
|
// duplicate key test
|
||||||
{
|
{
|
||||||
HashMap hm = {0};
|
HashMap hm = {0};
|
||||||
|
|
||||||
U64 a = 1;
|
U64 a = 1;
|
||||||
U64 b = 2;
|
U64 b = 2;
|
||||||
|
|
||||||
HashMapNode *n0 = hash_map_push_u64_u64(arena, &hm, 1, a);
|
HashMapNode *n0 = hash_map_push_u64_u64(arena, &hm, 1, a);
|
||||||
HashMapNode *n1 = hash_map_push_u64_u64(arena, &hm, 1, b);
|
HashMapNode *n1 = hash_map_push_u64_u64(arena, &hm, 1, b);
|
||||||
|
|
||||||
T_Ok(hm.count == 1);
|
T_Ok(hm.count == 1);
|
||||||
T_Ok(n0 == n1);
|
T_Ok(n0 == n1);
|
||||||
U64 test = *hash_map_search_u64_u64(&hm, 1);
|
U64 test = *hash_map_search_u64_u64(&hm, 1);
|
||||||
T_Ok(test != b);
|
T_Ok(test != b);
|
||||||
}
|
}
|
||||||
|
|
||||||
// test paths
|
// test paths
|
||||||
{
|
{
|
||||||
HashMap hm = {0};
|
HashMap hm = {0};
|
||||||
|
|
||||||
U64 foo = 0;
|
U64 foo = 0;
|
||||||
U64 bar = 1;
|
U64 bar = 1;
|
||||||
|
|
||||||
hash_map_push_path_raw(arena, &hm, str8_lit("c:/DEVEL/test"), &foo);
|
hash_map_push_path_raw(arena, &hm, str8_lit("c:/DEVEL/test"), &foo);
|
||||||
hash_map_push_path_raw(arena, &hm, str8_lit("c:\\DEVEL\\test"), &foo);
|
hash_map_push_path_raw(arena, &hm, str8_lit("c:\\DEVEL\\test"), &foo);
|
||||||
hash_map_push_path_raw(arena, &hm, str8_lit("c:/devel\\test"), &foo);
|
hash_map_push_path_raw(arena, &hm, str8_lit("c:/devel\\test"), &foo);
|
||||||
|
|
||||||
hash_map_push_path_raw(arena, &hm, str8_lit("/mnt/devel"), &bar);
|
hash_map_push_path_raw(arena, &hm, str8_lit("/mnt/devel"), &bar);
|
||||||
hash_map_push_path_raw(arena, &hm, str8_lit("/MNT/devel"), &bar);
|
hash_map_push_path_raw(arena, &hm, str8_lit("/MNT/devel"), &bar);
|
||||||
|
|
||||||
T_Ok(hm.count == 2);
|
T_Ok(hm.count == 2);
|
||||||
|
|
||||||
T_Ok(hash_map_search_path_raw(&hm, str8_lit("c:/devel/test")) == &foo);
|
T_Ok(hash_map_search_path_raw(&hm, str8_lit("c:/devel/test")) == &foo);
|
||||||
T_Ok(hash_map_search_path_raw(&hm, str8_lit("c:\\devel\\TEST")) == &foo);
|
T_Ok(hash_map_search_path_raw(&hm, str8_lit("c:\\devel\\TEST")) == &foo);
|
||||||
T_Ok(hash_map_search_path_raw(&hm, str8_lit("/mnt/devel")) == &bar);
|
T_Ok(hash_map_search_path_raw(&hm, str8_lit("/mnt/devel")) == &bar);
|
||||||
T_Ok(hash_map_search_path_raw(&hm, str8_lit("/MNT/DEVEL")) == &bar);
|
T_Ok(hash_map_search_path_raw(&hm, str8_lit("/MNT/DEVEL")) == &bar);
|
||||||
}
|
}
|
||||||
|
|
||||||
// test strings
|
// test strings
|
||||||
{
|
{
|
||||||
HashMap hm = {0};
|
HashMap hm = {0};
|
||||||
|
|
||||||
U64 foo = 0;
|
U64 foo = 0;
|
||||||
U64 bar = 1;
|
U64 bar = 1;
|
||||||
|
|
||||||
hash_map_push_string_raw(arena, &hm, str8_lit("c:/DEVEL/test"), &foo);
|
hash_map_push_string_raw(arena, &hm, str8_lit("c:/DEVEL/test"), &foo);
|
||||||
hash_map_push_string_raw(arena, &hm, str8_lit("c:\\DEVEL\\test"), &foo);
|
hash_map_push_string_raw(arena, &hm, str8_lit("c:\\DEVEL\\test"), &foo);
|
||||||
hash_map_push_string_raw(arena, &hm, str8_lit("c:/devel\\test"), &foo);
|
hash_map_push_string_raw(arena, &hm, str8_lit("c:/devel\\test"), &foo);
|
||||||
|
|
||||||
hash_map_push_string_raw(arena, &hm, str8_lit("/mnt/devel"), &bar);
|
hash_map_push_string_raw(arena, &hm, str8_lit("/mnt/devel"), &bar);
|
||||||
hash_map_push_string_raw(arena, &hm, str8_lit("/MNT/devel"), &bar);
|
hash_map_push_string_raw(arena, &hm, str8_lit("/MNT/devel"), &bar);
|
||||||
|
|
||||||
T_Ok(hm.count == 5);
|
T_Ok(hm.count == 5);
|
||||||
|
|
||||||
T_Ok(hash_map_search_string_raw(&hm, str8_lit("c:/DEVEL/test")) == &foo);
|
T_Ok(hash_map_search_string_raw(&hm, str8_lit("c:/DEVEL/test")) == &foo);
|
||||||
T_Ok(hash_map_search_string_raw(&hm, str8_lit("c:\\DEVEL\\test")) == &foo);
|
T_Ok(hash_map_search_string_raw(&hm, str8_lit("c:\\DEVEL\\test")) == &foo);
|
||||||
T_Ok(hash_map_search_string_raw(&hm, str8_lit("c:/devel\\test")) == &foo);
|
T_Ok(hash_map_search_string_raw(&hm, str8_lit("c:/devel\\test")) == &foo);
|
||||||
|
|
||||||
T_Ok(hash_map_search_string_raw(&hm, str8_lit("/mnt/devel")) == &bar);
|
T_Ok(hash_map_search_string_raw(&hm, str8_lit("/mnt/devel")) == &bar);
|
||||||
T_Ok(hash_map_search_string_raw(&hm, str8_lit("/MNT/devel")) == &bar);
|
T_Ok(hash_map_search_string_raw(&hm, str8_lit("/MNT/devel")) == &bar);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -644,14 +644,14 @@ TEST(merge)
|
|||||||
t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe /merge:.test=.test entry.obj test.obj");
|
t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe /merge:.test=.test entry.obj test.obj");
|
||||||
T_Ok(g_last_exit_code != 0);
|
T_Ok(g_last_exit_code != 0);
|
||||||
|
|
||||||
if (t_id_linker() == T_Linker_RAD) {
|
if (t_id_linker() == Linker_radlink) {
|
||||||
T_Ok(g_last_exit_code == LNK_Error_CircularMerge);
|
T_Ok(g_last_exit_code == LNK_Error_CircularMerge);
|
||||||
}
|
}
|
||||||
|
|
||||||
// circular merge with extra link
|
// circular merge with extra link
|
||||||
t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe /merge:.test=.data /merge:.data=.test entry.obj test.obj");
|
t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe /merge:.test=.data /merge:.data=.test entry.obj test.obj");
|
||||||
T_Ok(g_last_exit_code != 0);
|
T_Ok(g_last_exit_code != 0);
|
||||||
if (t_id_linker() == T_Linker_RAD) {
|
if (t_id_linker() == Linker_radlink) {
|
||||||
T_Ok(g_last_exit_code == LNK_Error_CircularMerge);
|
T_Ok(g_last_exit_code == LNK_Error_CircularMerge);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -677,14 +677,14 @@ TEST(merge)
|
|||||||
// illegal merge with .reloc
|
// illegal merge with .reloc
|
||||||
t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe /merge:.test=.reloc entry.obj test.obj");
|
t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe /merge:.test=.reloc entry.obj test.obj");
|
||||||
T_Ok(g_last_exit_code != 0);
|
T_Ok(g_last_exit_code != 0);
|
||||||
if (t_id_linker() == T_Linker_RAD) {
|
if (t_id_linker() == Linker_radlink) {
|
||||||
T_Ok(g_last_exit_code == LNK_Error_IllegalSectionMerge);
|
T_Ok(g_last_exit_code == LNK_Error_IllegalSectionMerge);
|
||||||
}
|
}
|
||||||
|
|
||||||
// illegal merge with .rsrc
|
// illegal merge with .rsrc
|
||||||
t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe /merge:.test=.rsrc entry.obj test.obj");
|
t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe /merge:.test=.rsrc entry.obj test.obj");
|
||||||
T_Ok(g_last_exit_code != 0);
|
T_Ok(g_last_exit_code != 0);
|
||||||
if (t_id_linker() == T_Linker_RAD) {
|
if (t_id_linker() == Linker_radlink) {
|
||||||
T_Ok(g_last_exit_code == LNK_Error_IllegalSectionMerge);
|
T_Ok(g_last_exit_code == LNK_Error_IllegalSectionMerge);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1784,7 +1784,7 @@ TEST(abs_vs_common)
|
|||||||
if (g_last_exit_code == 0) {
|
if (g_last_exit_code == 0) {
|
||||||
// TODO: validate that linker issues multiply defined symbol error
|
// TODO: validate that linker issues multiply defined symbol error
|
||||||
t_invoke_linkerf("/subsystem:console /entry:my_entry /out:a.exe common.obj abs.obj entry.obj");
|
t_invoke_linkerf("/subsystem:console /entry:my_entry /out:a.exe common.obj abs.obj entry.obj");
|
||||||
if (t_id_linker() == T_Linker_RAD) {
|
if (t_id_linker() == Linker_radlink) {
|
||||||
T_Ok(g_last_exit_code == LNK_Error_MultiplyDefinedSymbol);
|
T_Ok(g_last_exit_code == LNK_Error_MultiplyDefinedSymbol);
|
||||||
} else {
|
} else {
|
||||||
T_Ok(g_last_exit_code != 0);
|
T_Ok(g_last_exit_code != 0);
|
||||||
@@ -2092,7 +2092,7 @@ TEST(undef_reloc_section)
|
|||||||
T_Ok(t_write_file(str8_lit("sec_defn.obj"), sec_defn_obj));
|
T_Ok(t_write_file(str8_lit("sec_defn.obj"), sec_defn_obj));
|
||||||
|
|
||||||
t_invoke_linkerf("/subsystem:console /entry:my_entry /out:a.exe main.obj sec_defn.obj");
|
t_invoke_linkerf("/subsystem:console /entry:my_entry /out:a.exe main.obj sec_defn.obj");
|
||||||
if (t_id_linker() == T_Linker_RAD) {
|
if (t_id_linker() == Linker_radlink) {
|
||||||
T_Ok(g_last_exit_code == LNK_Error_SectRefsDiscardedMemory);
|
T_Ok(g_last_exit_code == LNK_Error_SectRefsDiscardedMemory);
|
||||||
} else {
|
} else {
|
||||||
T_Ok(g_last_exit_code != 0);
|
T_Ok(g_last_exit_code != 0);
|
||||||
@@ -2420,7 +2420,7 @@ TEST(base_relocs)
|
|||||||
|
|
||||||
// it is illegal to merge .reloc with other sections
|
// it is illegal to merge .reloc with other sections
|
||||||
t_invoke_linkerf("/subsystem:console /entry:my_entry /dynamicbase /largeaddressaware:no /out:a.exe /merge:.reloc=.rdata main.obj func.obj");
|
t_invoke_linkerf("/subsystem:console /entry:my_entry /dynamicbase /largeaddressaware:no /out:a.exe /merge:.reloc=.rdata main.obj func.obj");
|
||||||
if (t_id_linker() == T_Linker_RAD) {
|
if (t_id_linker() == Linker_radlink) {
|
||||||
T_Ok(g_last_exit_code == LNK_Error_IllegalSectionMerge);
|
T_Ok(g_last_exit_code == LNK_Error_IllegalSectionMerge);
|
||||||
} else {
|
} else {
|
||||||
T_Ok(g_last_exit_code != 0);
|
T_Ok(g_last_exit_code != 0);
|
||||||
@@ -2428,7 +2428,7 @@ TEST(base_relocs)
|
|||||||
|
|
||||||
// the other way around is illegal too
|
// the other way around is illegal too
|
||||||
t_invoke_linkerf("/subsystem:console /entry:my_entry /dynamicbase /largeaddressaware:no /out:a.exe /merge:.rdata=.reloc main.obj func.obj");
|
t_invoke_linkerf("/subsystem:console /entry:my_entry /dynamicbase /largeaddressaware:no /out:a.exe /merge:.rdata=.reloc main.obj func.obj");
|
||||||
if (t_id_linker() == T_Linker_RAD) {
|
if (t_id_linker() == Linker_radlink) {
|
||||||
T_Ok(g_last_exit_code == LNK_Error_IllegalSectionMerge);
|
T_Ok(g_last_exit_code == LNK_Error_IllegalSectionMerge);
|
||||||
} else {
|
} else {
|
||||||
T_Ok(g_last_exit_code != 0);
|
T_Ok(g_last_exit_code != 0);
|
||||||
@@ -2610,7 +2610,7 @@ TEST(import_export)
|
|||||||
T_Ok(g_last_exit_code == 0);
|
T_Ok(g_last_exit_code == 0);
|
||||||
|
|
||||||
// validate export table in export.dll
|
// validate export table in export.dll
|
||||||
if (t_id_linker() == T_Linker_RAD) {
|
if (t_id_linker() == Linker_radlink) {
|
||||||
// validate export table in export.dll
|
// validate export table in export.dll
|
||||||
{
|
{
|
||||||
String8 dll = t_read_file(arena, str8_lit("export.dll"));
|
String8 dll = t_read_file(arena, str8_lit("export.dll"));
|
||||||
@@ -2905,7 +2905,7 @@ TEST(comdat_no_duplicates)
|
|||||||
|
|
||||||
t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe a.obj b.obj entry.obj");
|
t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe a.obj b.obj entry.obj");
|
||||||
T_Ok(g_last_exit_code != 0);
|
T_Ok(g_last_exit_code != 0);
|
||||||
if (t_id_linker() == T_Linker_RAD) { T_Ok(g_last_exit_code == LNK_Error_MultiplyDefinedSymbol); }
|
if (t_id_linker() == Linker_radlink) { T_Ok(g_last_exit_code == LNK_Error_MultiplyDefinedSymbol); }
|
||||||
|
|
||||||
t_invoke_linkerf("/subsystem:console /entry:entry /out:b.exe a.obj entry.obj");
|
t_invoke_linkerf("/subsystem:console /entry:entry /out:b.exe a.obj entry.obj");
|
||||||
T_Ok(g_last_exit_code == 0);
|
T_Ok(g_last_exit_code == 0);
|
||||||
@@ -3001,7 +3001,7 @@ TEST(comdat_same_size)
|
|||||||
|
|
||||||
t_invoke_linkerf("/subsystem:console /entry:entry /out:b.exe a.obj b.obj c.obj entry.obj");
|
t_invoke_linkerf("/subsystem:console /entry:entry /out:b.exe a.obj b.obj c.obj entry.obj");
|
||||||
T_Ok(g_last_exit_code != 0);
|
T_Ok(g_last_exit_code != 0);
|
||||||
if (t_id_linker() == T_Linker_RAD) { T_Ok(g_last_exit_code == LNK_Error_MultiplyDefinedSymbol); }
|
if (t_id_linker() == Linker_radlink) { T_Ok(g_last_exit_code == LNK_Error_MultiplyDefinedSymbol); }
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(comdat_exact_match)
|
TEST(comdat_exact_match)
|
||||||
@@ -3313,7 +3313,7 @@ TEST(comdat_associative_loop)
|
|||||||
|
|
||||||
t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe loop.obj entry.obj");
|
t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe loop.obj entry.obj");
|
||||||
T_Ok(g_last_exit_code != 0);
|
T_Ok(g_last_exit_code != 0);
|
||||||
if (t_id_linker() == T_Linker_RAD) { T_Ok(g_last_exit_code == LNK_Error_AssociativeLoop); }
|
if (t_id_linker() == Linker_radlink) { T_Ok(g_last_exit_code == LNK_Error_AssociativeLoop); }
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(comdat_associative_non_comdat)
|
TEST(comdat_associative_non_comdat)
|
||||||
@@ -3430,7 +3430,7 @@ TEST(comdat_associative_out_of_bounds)
|
|||||||
|
|
||||||
t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe entry.obj bad.obj");
|
t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe entry.obj bad.obj");
|
||||||
T_Ok(g_last_exit_code != 0);
|
T_Ok(g_last_exit_code != 0);
|
||||||
if (t_id_linker() == T_Linker_RAD) { T_Ok(g_last_exit_code == LNK_Error_IllData); }
|
if (t_id_linker() == Linker_radlink) { T_Ok(g_last_exit_code == LNK_Error_IllData); }
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(comdat_with_offset)
|
TEST(comdat_with_offset)
|
||||||
@@ -3790,7 +3790,7 @@ TEST(include)
|
|||||||
// test unresolved include
|
// test unresolved include
|
||||||
t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe /include:ewq entry.obj");
|
t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe /include:ewq entry.obj");
|
||||||
T_Ok(g_last_exit_code != 0);
|
T_Ok(g_last_exit_code != 0);
|
||||||
if (t_id_linker() == T_Linker_RAD) { T_Ok(g_last_exit_code == LNK_Error_UnresolvedSymbol); }
|
if (t_id_linker() == Linker_radlink) { T_Ok(g_last_exit_code == LNK_Error_UnresolvedSymbol); }
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(communal_var_vs_regular)
|
TEST(communal_var_vs_regular)
|
||||||
@@ -4811,7 +4811,7 @@ TEST(fail_if_mismatch)
|
|||||||
T_Ok(t_write_file(str8_lit("a2.obj"), a2));
|
T_Ok(t_write_file(str8_lit("a2.obj"), a2));
|
||||||
|
|
||||||
t_invoke_linkerf("entry.obj a1.obj a2.obj /entry:entry /subsystem:console /out:a2.exe");
|
t_invoke_linkerf("entry.obj a1.obj a2.obj /entry:entry /subsystem:console /out:a2.exe");
|
||||||
if (t_id_linker() == T_Linker_RAD) T_Ok(g_last_exit_code == LNK_Error_FailIfMismatch);
|
if (t_id_linker() == Linker_radlink) T_Ok(g_last_exit_code == LNK_Error_FailIfMismatch);
|
||||||
else T_Ok(g_last_exit_code != 0);
|
else T_Ok(g_last_exit_code != 0);
|
||||||
|
|
||||||
// ------------------------------------------------------------
|
// ------------------------------------------------------------
|
||||||
@@ -4829,14 +4829,14 @@ TEST(fail_if_mismatch)
|
|||||||
T_Ok(t_write_file(str8_lit("conf_dirs.obj"), conf_dirs));
|
T_Ok(t_write_file(str8_lit("conf_dirs.obj"), conf_dirs));
|
||||||
|
|
||||||
t_invoke_linkerf("entry.obj conf_dirs.obj /entry:entry /subsystem:console /out:conf_dirs.exe");
|
t_invoke_linkerf("entry.obj conf_dirs.obj /entry:entry /subsystem:console /out:conf_dirs.exe");
|
||||||
if (t_id_linker() == T_Linker_RAD) T_Ok(g_last_exit_code == LNK_Error_FailIfMismatch);
|
if (t_id_linker() == Linker_radlink) T_Ok(g_last_exit_code == LNK_Error_FailIfMismatch);
|
||||||
else T_Ok(g_last_exit_code != 0);
|
else T_Ok(g_last_exit_code != 0);
|
||||||
|
|
||||||
// ------------------------------------------------------------
|
// ------------------------------------------------------------
|
||||||
// passing switch on command line
|
// passing switch on command line
|
||||||
|
|
||||||
t_invoke_linkerf("entry.obj a1.obj /FAILIFMISMATCH:a=2 /out:cmddir.exe");
|
t_invoke_linkerf("entry.obj a1.obj /FAILIFMISMATCH:a=2 /out:cmddir.exe");
|
||||||
if (t_id_linker() == T_Linker_RAD) T_Ok(g_last_exit_code == LNK_Error_FailIfMismatch);
|
if (t_id_linker() == Linker_radlink) T_Ok(g_last_exit_code == LNK_Error_FailIfMismatch);
|
||||||
else T_Ok(g_last_exit_code != 0);
|
else T_Ok(g_last_exit_code != 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -127,6 +127,17 @@ lnx_thread_entry_point(void *ptr)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
//~ rjf: @per_os_impl Debugger Attachment Checking
|
||||||
|
|
||||||
|
internal B32
|
||||||
|
debugger_is_attached(void)
|
||||||
|
{
|
||||||
|
B32 result = 0;
|
||||||
|
// TODO(rjf)
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
////////////////////////////////
|
////////////////////////////////
|
||||||
//~ rjf: @per_os_impl Platform Time Functions
|
//~ rjf: @per_os_impl Platform Time Functions
|
||||||
|
|
||||||
@@ -1292,13 +1303,13 @@ internal B32
|
|||||||
process_join(Process process, U64 endt_us, U64 *exit_code_out)
|
process_join(Process process, U64 endt_us, U64 *exit_code_out)
|
||||||
{
|
{
|
||||||
B32 result = 0;
|
B32 result = 0;
|
||||||
|
|
||||||
pid_t pid = (pid_t)process.u64[0];
|
pid_t pid = (pid_t)process.u64[0];
|
||||||
for(;;)
|
for(;;)
|
||||||
{
|
{
|
||||||
int status = 0;
|
int status = 0;
|
||||||
pid_t wait_result = LNX_RETRY_ON_EINTR(waitpid(pid, &status, (endt_us == max_U64) ? 0 : WNOHANG));
|
pid_t wait_result = LNX_RETRY_ON_EINTR(waitpid(pid, &status, (endt_us == max_U64) ? 0 : WNOHANG));
|
||||||
|
|
||||||
if((wait_result == pid) && (WIFEXITED(status) || WIFSIGNALED(status)))
|
if((wait_result == pid) && (WIFEXITED(status) || WIFSIGNALED(status)))
|
||||||
{
|
{
|
||||||
result = 1;
|
result = 1;
|
||||||
@@ -1309,13 +1320,13 @@ process_join(Process process, U64 endt_us, U64 *exit_code_out)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(wait_result == -1) { break; }
|
if(wait_result == -1) { break; }
|
||||||
if(endt_us == 0) { break; }
|
if(endt_us == 0) { break; }
|
||||||
|
|
||||||
U64 now_us = now_time_us();
|
U64 now_us = now_time_us();
|
||||||
if(now_us >= endt_us) { break; }
|
if(now_us >= endt_us) { break; }
|
||||||
|
|
||||||
U64 left_us = endt_us - now_us;
|
U64 left_us = endt_us - now_us;
|
||||||
U64 sleep_us = Min(left_us, Thousand(1));
|
U64 sleep_us = Min(left_us, Thousand(1));
|
||||||
usleep((useconds_t)sleep_us);
|
usleep((useconds_t)sleep_us);
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#define T_Group "MD"
|
// Copyright (c) Epic Games Tools
|
||||||
|
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||||
|
|
||||||
SKIP(md_tokenizer)
|
SkippedTest(md_tokenizer)
|
||||||
{
|
{
|
||||||
MD_TokenizeResult result;
|
MD_TokenizeResult result;
|
||||||
|
|
||||||
@@ -126,5 +127,3 @@ SKIP(md_tokenizer)
|
|||||||
T_Ok(result.tokens.v[1].flags & MD_TokenFlag_Identifier);
|
T_Ok(result.tokens.v[1].flags & MD_TokenFlag_Identifier);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#undef T_Group
|
|
||||||
+37
-37
@@ -1,37 +1,37 @@
|
|||||||
/// test: {
|
/// test: {
|
||||||
/// windows: {
|
/// windows: {
|
||||||
/// compile: "/Od /Z7 /c main.c module.c"
|
/// compile: "/Od /Z7 /c main.c module.c"
|
||||||
/// link: "/fixed /debug:full /dll module.obj"
|
/// link: "/fixed /debug:full /dll module.obj"
|
||||||
/// link: "/fixed /debug:full module.lib main.obj /out:main.exe /incremental:no"
|
/// link: "/fixed /debug:full module.lib main.obj /out:main.exe /incremental:no"
|
||||||
/// launch: "main.exe"
|
/// launch: "main.exe"
|
||||||
/// }
|
/// }
|
||||||
/// linux: {
|
/// linux: {
|
||||||
/// compile: "-O0 -g module.c -fPIC -shared -o libmodule.so"
|
/// compile: "-O0 -g module.c -fPIC -shared -o libmodule.so"
|
||||||
/// compile: "-O0 -g main.c -L. -lmodule -o main -Wl,-rpath,%CWD%,-z,now -fno-plt"
|
/// compile: "-O0 -g main.c -L. -lmodule -o main -Wl,-rpath,%CWD%,-z,now -fno-plt"
|
||||||
/// launch: "./main"
|
/// launch: "./main"
|
||||||
/// }
|
/// }
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
|
|
||||||
/// file: "module.c"
|
/// file: "module.c"
|
||||||
|
|
||||||
#if _WIN32
|
#if _WIN32
|
||||||
__declspec(dllexport)
|
__declspec(dllexport)
|
||||||
#endif
|
#endif
|
||||||
int foo(int a)
|
int foo(int a)
|
||||||
{ /// 3: at
|
{ /// 3: at
|
||||||
return a + 1;
|
return a + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// file: "main.c"
|
/// file: "main.c"
|
||||||
|
|
||||||
#if _WIN32
|
#if _WIN32
|
||||||
__declspec(dllimport)
|
__declspec(dllimport)
|
||||||
#endif
|
#endif
|
||||||
int foo();
|
int foo();
|
||||||
|
|
||||||
int main()
|
int main()
|
||||||
{ /// 1: { step_into, at, step_over }
|
{ /// 1: { step_into, at, step_over }
|
||||||
foo(123); /// 2: { at, step_into }
|
foo(123); /// 2: { at, step_into }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1,6 +1,207 @@
|
|||||||
// Copyright (c) Epic Games Tools
|
// Copyright (c) Epic Games Tools
|
||||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
// IPC Controller
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
B32 running;
|
||||||
|
U64 run_gen;
|
||||||
|
U64 ip;
|
||||||
|
Arch arch;
|
||||||
|
U64 vaddr_min;
|
||||||
|
U64 vaddr_max;
|
||||||
|
U64 ip_vaddr;
|
||||||
|
U64 sp_base;
|
||||||
|
U64 tls_root;
|
||||||
|
U64 tls_index;
|
||||||
|
U64 tls_offset;
|
||||||
|
U64 timestamp;
|
||||||
|
U64 exception_code;
|
||||||
|
U64 bp_flags;
|
||||||
|
String8 string;
|
||||||
|
OperatingSystem target_os;
|
||||||
|
U64 tls_model;
|
||||||
|
String8 stop_cause;
|
||||||
|
} T_DbgState;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
String8 file_path;
|
||||||
|
TxtPt pt;
|
||||||
|
Rng1U64 voff_range;
|
||||||
|
} T_DbgLine;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
U64 count;
|
||||||
|
T_DbgLine *v;
|
||||||
|
} T_DbgLineArray;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
String8 expr;
|
||||||
|
String8 value;
|
||||||
|
String8 type;
|
||||||
|
String8 error;
|
||||||
|
} T_Eval;
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
// Dbg Script
|
||||||
|
|
||||||
|
#define T_DbgScriptCmdKind_XList \
|
||||||
|
X(Breakpoint, "bp") \
|
||||||
|
X(ClearBreakpoints, "bp_clear") \
|
||||||
|
X(Halt, "halt") \
|
||||||
|
X(Run, "run") \
|
||||||
|
X(RunToLine, "run_to_line") \
|
||||||
|
X(StepOver, "step_over") \
|
||||||
|
X(StepInto, "step_into") \
|
||||||
|
X(StepOut, "step_out") \
|
||||||
|
X(StepOverInst, "step_over_inst") \
|
||||||
|
X(StepIntoInst, "step_over_inst") \
|
||||||
|
X(StepOverLine, "step_over_line") \
|
||||||
|
X(StepIntoLine, "step_into_line") \
|
||||||
|
X(KillAll, "kll_all") \
|
||||||
|
X(At, "at") \
|
||||||
|
X(Eval, "eval")
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
T_DbgScriptCmdKind_Null,
|
||||||
|
#define X(n,...) T_DbgScriptCmdKind_##n,
|
||||||
|
T_DbgScriptCmdKind_XList
|
||||||
|
#undef X
|
||||||
|
T_DbgScriptCmdKind_Count
|
||||||
|
} T_DbgScriptCmdKind;
|
||||||
|
|
||||||
|
#define T_DbgScriptDirectiveKind_XList \
|
||||||
|
X(Compile, "compile") \
|
||||||
|
X(Link, "link") \
|
||||||
|
X(Launch, "launch") \
|
||||||
|
X(Skip, "skip")
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
T_DbgScriptDirectiveKind_Null,
|
||||||
|
#define X(q,w) T_DbgScriptDirectiveKind_##q,
|
||||||
|
T_DbgScriptDirectiveKind_XList
|
||||||
|
#undef X
|
||||||
|
T_DbgScriptDirectiveKind_Count,
|
||||||
|
} T_DbgScriptDirectiveKind;
|
||||||
|
|
||||||
|
typedef struct T_DbgScriptFile
|
||||||
|
{
|
||||||
|
struct T_DbgScriptFile *next;
|
||||||
|
String8 path;
|
||||||
|
String8 source;
|
||||||
|
TxtPt pt;
|
||||||
|
} T_DbgScriptFile;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
U64 count;
|
||||||
|
T_DbgScriptFile *first;
|
||||||
|
T_DbgScriptFile *last;
|
||||||
|
} T_DbgScriptFileList;
|
||||||
|
|
||||||
|
typedef struct T_DbgScriptCmd
|
||||||
|
{
|
||||||
|
struct T_DbgScriptCmd *next;
|
||||||
|
T_DbgScriptCmdKind kind;
|
||||||
|
TxtPt pt;
|
||||||
|
S64 at_delta;
|
||||||
|
} T_DbgScriptCmd;
|
||||||
|
|
||||||
|
typedef struct T_DbgScriptProgram
|
||||||
|
{
|
||||||
|
TxtPt pt;
|
||||||
|
U64 order;
|
||||||
|
OperatingSystem os;
|
||||||
|
T_DbgScriptFile *file;
|
||||||
|
U64 count;
|
||||||
|
T_DbgScriptCmd *first;
|
||||||
|
T_DbgScriptCmd *last;
|
||||||
|
struct T_DbgScriptProgram *next;
|
||||||
|
} T_DbgScriptProgram;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
U64 count;
|
||||||
|
T_DbgScriptProgram *first;
|
||||||
|
T_DbgScriptProgram *last;
|
||||||
|
} T_DbgScriptProgramList;
|
||||||
|
|
||||||
|
typedef struct T_DbgScriptDirective
|
||||||
|
{
|
||||||
|
struct T_DbgScriptDirective *next;
|
||||||
|
T_DbgScriptDirectiveKind kind;
|
||||||
|
TxtPt pt;
|
||||||
|
String8 args;
|
||||||
|
union {
|
||||||
|
struct {
|
||||||
|
Compiler cc;
|
||||||
|
} compile;
|
||||||
|
};
|
||||||
|
} T_DbgScriptDirective;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
U64 count;
|
||||||
|
T_DbgScriptDirective *first;
|
||||||
|
T_DbgScriptDirective *last;
|
||||||
|
} T_DbgScriptDirectiveList;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
String8 file_path;
|
||||||
|
T_DbgScriptDirectiveList directives[OperatingSystem_COUNT][T_DbgScriptDirectiveKind_Count];
|
||||||
|
T_DbgScriptFileList files;
|
||||||
|
U64 program_count;
|
||||||
|
T_DbgScriptProgram **programs;
|
||||||
|
B32 skip;
|
||||||
|
} T_DbgScript;
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
// IPC Controller
|
||||||
|
|
||||||
|
// error helper
|
||||||
|
internal void t_errorf_md(String8 file_name, String8 source, MD_Node *n, char *fmt, ...);
|
||||||
|
|
||||||
|
// reply parse helpers
|
||||||
|
internal B32 t_ipc_parse_string(MD_Node *node, String8 child_name, String8 *out);
|
||||||
|
internal B32 t_ipc_parse_u32(MD_Node *node, String8 child_name, U32 *out);
|
||||||
|
internal B32 t_ipc_parse_int_(MD_Node *node, String8 child_name, U64 out_size, void *out);
|
||||||
|
internal B32 t_ipc_parse_b32(MD_Node *node, String8 child_name, B32 *out);
|
||||||
|
#define t_ipc_parse_int(n, c, ptr) t_ipc_parse_int_(n, c, sizeof(*ptr), ptr)
|
||||||
|
|
||||||
|
// debugger commands
|
||||||
|
internal B32 t_dbg_send_cmd(String8 cmd, U64 timeout_us, Arena *reply_arena, MD_ParseResult *reply_out);
|
||||||
|
internal B32 t_dbg_send_cmdf(U64 timeout_us, Arena *reply_arena, MD_ParseResult *reply_out, char *fmt, ...);
|
||||||
|
internal T_DbgState * t_dbg_state(Arena *arena, U64 timeout_us);
|
||||||
|
internal B32 t_dbg_src_line(Arena *arena, U64 vaddr, T_DbgLineArray *lines_out, U64 timeout_us);
|
||||||
|
internal String8 t_dbg_value_from_expr(Arena *arena, String8 expr);
|
||||||
|
internal String8 t_dbg_value_from_exprf(Arena *arena, char *fmt, ...);
|
||||||
|
internal B32 t_dbg_send_cmd_and_wait_stop(String8 cmd, U64 timeout_us);
|
||||||
|
internal B32 t_dbg_ping(U64 timeout_us);
|
||||||
|
internal B32 t_dbg_bp_add_line(String8 file, U64 line);
|
||||||
|
internal B32 t_dbg_bp_add_func(String8 func_name);
|
||||||
|
internal B32 t_dbg_bp_add_addr(U64 addr);
|
||||||
|
internal B32 t_dbg_launch(String8 cmdline, U64 timeout_us);
|
||||||
|
internal B32 t_dbg_eval(Arena *arena, String8 expr, T_Eval *eval_out);
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
// Dbg Script
|
||||||
|
|
||||||
|
internal String8 t_string_from_dbg_script_cmd_kind(T_DbgScriptCmdKind v);
|
||||||
|
internal T_DbgScriptCmdKind t_dbg_script_cmd_kind_from_string(String8 cmd);
|
||||||
|
internal B32 t_dbg_parse_script(Arena *arena, String8 file_path, String8 source, T_DbgScript *script_out);
|
||||||
|
internal B32 t_dbg_script_invoke(T_DbgScript *script, U64 timeout_us);
|
||||||
|
internal void t_dbg_register_script_tests(Arena *arena, String8 folder_path);
|
||||||
|
|
||||||
|
|
||||||
#define T_Dbg_DefaultTimeout TIMEOUT_SEC(5)
|
#define T_Dbg_DefaultTimeout TIMEOUT_SEC(5)
|
||||||
|
|
||||||
extern B32 g_stop_on_first_fail_or_crash;
|
extern B32 g_stop_on_first_fail_or_crash;
|
||||||
@@ -119,7 +320,7 @@ t_dbg_send_cmd(String8 cmd, U64 timeout_us, Arena *reply_arena, MD_ParseResult *
|
|||||||
// parse reply
|
// parse reply
|
||||||
Arena *a = reply_arena ? reply_arena : scratch.arena;
|
Arena *a = reply_arena ? reply_arena : scratch.arena;
|
||||||
String8 reply_text = str8_copy(a, g_output);
|
String8 reply_text = str8_copy(a, g_output);
|
||||||
|
|
||||||
t_infof("IPC-Reply: \"%S\"\n", reply_text);
|
t_infof("IPC-Reply: \"%S\"\n", reply_text);
|
||||||
MD_ParseResult reply = md_parse_from_text(a, str8_lit("ipc_reply"), reply_text);
|
MD_ParseResult reply = md_parse_from_text(a, str8_lit("ipc_reply"), reply_text);
|
||||||
|
|
||||||
@@ -153,13 +354,13 @@ internal T_DbgState *
|
|||||||
t_dbg_state(Arena *arena, U64 timeout_us)
|
t_dbg_state(Arena *arena, U64 timeout_us)
|
||||||
{
|
{
|
||||||
Temp scratch = scratch_begin(&arena, 1);
|
Temp scratch = scratch_begin(&arena, 1);
|
||||||
|
|
||||||
T_DbgState *result = 0;
|
T_DbgState *result = 0;
|
||||||
|
|
||||||
// send status request
|
// send status request
|
||||||
MD_ParseResult reply = {0};
|
MD_ParseResult reply = {0};
|
||||||
if ( ! t_dbg_send_cmd(str8_lit("state"), timeout_us, arena, &reply)) { goto exit; }
|
if ( ! t_dbg_send_cmd(str8_lit("state"), timeout_us, arena, &reply)) { goto exit; }
|
||||||
|
|
||||||
// parse reply
|
// parse reply
|
||||||
MD_Node *state_md = md_child_from_string(reply.root, str8_lit("state"), 0);
|
MD_Node *state_md = md_child_from_string(reply.root, str8_lit("state"), 0);
|
||||||
MD_Node *stop_event_md = md_child_from_string(state_md, str8_lit("stop_event"), 0);
|
MD_Node *stop_event_md = md_child_from_string(state_md, str8_lit("stop_event"), 0);
|
||||||
@@ -186,14 +387,14 @@ t_dbg_src_line(Arena *arena, U64 vaddr, T_DbgLineArray *lines_out, U64 timeout_u
|
|||||||
// send line map request
|
// send line map request
|
||||||
MD_ParseResult reply = {0};
|
MD_ParseResult reply = {0};
|
||||||
if(!t_dbg_send_cmdf(timeout_us, arena, &reply, "line_from_vaddr 0x%llx", vaddr)) { goto exit; }
|
if(!t_dbg_send_cmdf(timeout_us, arena, &reply, "line_from_vaddr 0x%llx", vaddr)) { goto exit; }
|
||||||
|
|
||||||
// parse reply
|
// parse reply
|
||||||
MD_Node *lines_md = md_child_from_string(reply.root, str8_lit("lines"), 0);
|
MD_Node *lines_md = md_child_from_string(reply.root, str8_lit("lines"), 0);
|
||||||
|
|
||||||
typedef struct Node { struct Node *next; T_DbgLine v; } Node;
|
typedef struct Node { struct Node *next; T_DbgLine v; } Node;
|
||||||
Node *first_line = 0, *last_line = 0;
|
Node *first_line = 0, *last_line = 0;
|
||||||
U64 line_count = 0;
|
U64 line_count = 0;
|
||||||
|
|
||||||
for MD_EachNode(n, lines_md->first) {
|
for MD_EachNode(n, lines_md->first) {
|
||||||
T_DbgLine line = {0};
|
T_DbgLine line = {0};
|
||||||
if ( ! t_ipc_parse_string(n, str8_lit("file_path"), &line.file_path)) { t_infof("INFO: 'lines' is missing 'file_path'\n"); goto exit; }
|
if ( ! t_ipc_parse_string(n, str8_lit("file_path"), &line.file_path)) { t_infof("INFO: 'lines' is missing 'file_path'\n"); goto exit; }
|
||||||
@@ -201,13 +402,13 @@ t_dbg_src_line(Arena *arena, U64 vaddr, T_DbgLineArray *lines_out, U64 timeout_u
|
|||||||
if ( ! t_ipc_parse_int (n, str8_lit("column_num"), &line.pt.column)) { t_infof("INFO: 'lines' is missing 'column_num'\n"); goto exit; }
|
if ( ! t_ipc_parse_int (n, str8_lit("column_num"), &line.pt.column)) { t_infof("INFO: 'lines' is missing 'column_num'\n"); goto exit; }
|
||||||
if ( ! t_ipc_parse_int (n, str8_lit("voff_range_min"), &line.voff_range.min)) { t_infof("INFO: 'lines' is missing 'voff_range_min'\n"); goto exit; }
|
if ( ! t_ipc_parse_int (n, str8_lit("voff_range_min"), &line.voff_range.min)) { t_infof("INFO: 'lines' is missing 'voff_range_min'\n"); goto exit; }
|
||||||
if ( ! t_ipc_parse_int (n, str8_lit("voff_range_max"), &line.voff_range.max)) { t_infof("INFO: 'lines' is missing 'voff_range_max'\n"); goto exit; }
|
if ( ! t_ipc_parse_int (n, str8_lit("voff_range_max"), &line.voff_range.max)) { t_infof("INFO: 'lines' is missing 'voff_range_max'\n"); goto exit; }
|
||||||
|
|
||||||
Node *n = push_array(scratch.arena, Node, 1);
|
Node *n = push_array(scratch.arena, Node, 1);
|
||||||
n->v = line;
|
n->v = line;
|
||||||
SLLQueuePush(first_line, last_line, n);
|
SLLQueuePush(first_line, last_line, n);
|
||||||
line_count += 1;
|
line_count += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lines_out && line_count > 0) {
|
if (lines_out && line_count > 0) {
|
||||||
lines_out->count = 0;
|
lines_out->count = 0;
|
||||||
lines_out->v = push_array(arena, T_DbgLine, line_count);
|
lines_out->v = push_array(arena, T_DbgLine, line_count);
|
||||||
@@ -479,9 +680,9 @@ internal B32
|
|||||||
t_dbg_parse_script(Arena *arena, String8 file_path, String8 source, T_DbgScript *script_out)
|
t_dbg_parse_script(Arena *arena, String8 file_path, String8 source, T_DbgScript *script_out)
|
||||||
{
|
{
|
||||||
Temp scratch = scratch_begin(&arena, 1);
|
Temp scratch = scratch_begin(&arena, 1);
|
||||||
|
|
||||||
B32 is_ok = 0;
|
B32 is_ok = 0;
|
||||||
|
|
||||||
T_DbgScript script = { .file_path = push_str8_copy(arena, file_path) };
|
T_DbgScript script = { .file_path = push_str8_copy(arena, file_path) };
|
||||||
|
|
||||||
// parse out mdesk out of the comments
|
// parse out mdesk out of the comments
|
||||||
@@ -489,41 +690,41 @@ t_dbg_parse_script(Arena *arena, String8 file_path, String8 source, T_DbgScript
|
|||||||
MD_TokenChunkList annot_chunks = {0};
|
MD_TokenChunkList annot_chunks = {0};
|
||||||
for EachIndex(token_idx, source_tokens.tokens.count) {
|
for EachIndex(token_idx, source_tokens.tokens.count) {
|
||||||
MD_Token source_token = source_tokens.tokens.v[token_idx];
|
MD_Token source_token = source_tokens.tokens.v[token_idx];
|
||||||
|
|
||||||
// skip non-comment tokens
|
// skip non-comment tokens
|
||||||
if (~source_token.flags & MD_TokenFlag_Comment) { continue; }
|
if (~source_token.flags & MD_TokenFlag_Comment) { continue; }
|
||||||
|
|
||||||
// read token string
|
// read token string
|
||||||
String8 source_token_string = str8_substr(source, source_token.range);
|
String8 source_token_string = str8_substr(source, source_token.range);
|
||||||
|
|
||||||
if (str8_match_wildcard(source_token_string, str8_lit("*///*"), 0)) {
|
if (str8_match_wildcard(source_token_string, str8_lit("*///*"), 0)) {
|
||||||
|
|
||||||
// drop comment prefix
|
// drop comment prefix
|
||||||
String8 raw_annots = str8_skip(str8_skip_chop_whitespace(source_token_string), 3);
|
String8 raw_annots = str8_skip(str8_skip_chop_whitespace(source_token_string), 3);
|
||||||
|
|
||||||
// parse annotations
|
// parse annotations
|
||||||
MD_TokenizeResult annot_parse = md_tokenize_from_text(scratch.arena, raw_annots);
|
MD_TokenizeResult annot_parse = md_tokenize_from_text(scratch.arena, raw_annots);
|
||||||
|
|
||||||
for EachIndex(i, annot_parse.tokens.count) {
|
for EachIndex(i, annot_parse.tokens.count) {
|
||||||
MD_Token annot_token = annot_parse.tokens.v[i];
|
MD_Token annot_token = annot_parse.tokens.v[i];
|
||||||
|
|
||||||
// adjust token range so they point back into the source file
|
// adjust token range so they point back into the source file
|
||||||
annot_token.range = shift_1u64(annot_token.range, (U64)(raw_annots.str - source.str));
|
annot_token.range = shift_1u64(annot_token.range, (U64)(raw_annots.str - source.str));
|
||||||
|
|
||||||
// append annotation token
|
// append annotation token
|
||||||
md_token_chunk_list_push(scratch.arena, &annot_chunks, 4096, annot_token);
|
md_token_chunk_list_push(scratch.arena, &annot_chunks, 4096, annot_token);
|
||||||
}
|
}
|
||||||
|
|
||||||
// append new line token
|
// append new line token
|
||||||
MD_Token newline_token = md_token_make(r1u64(source_token.range.max, source_token.range.max), MD_TokenFlag_Newline);
|
MD_Token newline_token = md_token_make(r1u64(source_token.range.max, source_token.range.max), MD_TokenFlag_Newline);
|
||||||
md_token_chunk_list_push(scratch.arena, &annot_chunks, 4096, newline_token);
|
md_token_chunk_list_push(scratch.arena, &annot_chunks, 4096, newline_token);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// script annotations -> mdesk tree
|
// script annotations -> mdesk tree
|
||||||
MD_TokenArray annot_tokens = md_token_array_from_chunk_list(scratch.arena, &annot_chunks);
|
MD_TokenArray annot_tokens = md_token_array_from_chunk_list(scratch.arena, &annot_chunks);
|
||||||
MD_ParseResult script_parse = md_parse_from_text_tokens(scratch.arena, file_path, source, annot_tokens);
|
MD_ParseResult script_parse = md_parse_from_text_tokens(scratch.arena, file_path, source, annot_tokens);
|
||||||
|
|
||||||
// was parse ok? -> error
|
// was parse ok? -> error
|
||||||
if (script_parse.msgs.worst_message_kind >= MD_MsgKind_Error) {
|
if (script_parse.msgs.worst_message_kind >= MD_MsgKind_Error) {
|
||||||
t_errorf("ERROR: cannot tokenize mdesk file: \"%S\"\n", file_path);
|
t_errorf("ERROR: cannot tokenize mdesk file: \"%S\"\n", file_path);
|
||||||
@@ -531,11 +732,11 @@ t_dbg_parse_script(Arena *arena, String8 file_path, String8 source, T_DbgScript
|
|||||||
String8 msg_kind_string = {0};
|
String8 msg_kind_string = {0};
|
||||||
switch(msg->kind)
|
switch(msg->kind)
|
||||||
{
|
{
|
||||||
default:{}break;
|
default:{}break;
|
||||||
case MD_MsgKind_Note: {msg_kind_string = str8_lit("note");}break;
|
case MD_MsgKind_Note: {msg_kind_string = str8_lit("note");}break;
|
||||||
case MD_MsgKind_Warning: {msg_kind_string = str8_lit("warning");}break;
|
case MD_MsgKind_Warning: {msg_kind_string = str8_lit("warning");}break;
|
||||||
case MD_MsgKind_Error: {msg_kind_string = str8_lit("error");}break;
|
case MD_MsgKind_Error: {msg_kind_string = str8_lit("error");}break;
|
||||||
case MD_MsgKind_FatalError: {msg_kind_string = str8_lit("fatal error");}break;
|
case MD_MsgKind_FatalError: {msg_kind_string = str8_lit("fatal error");}break;
|
||||||
}
|
}
|
||||||
TxtPt pt = mg_txt_pt_from_string_off(source, msg->node->src_offset);
|
TxtPt pt = mg_txt_pt_from_string_off(source, msg->node->src_offset);
|
||||||
String8 loc = push_str8f(scratch.arena, "%S:%I64d:%I64d", file_path, pt.line, pt.column);
|
String8 loc = push_str8f(scratch.arena, "%S:%I64d:%I64d", file_path, pt.line, pt.column);
|
||||||
@@ -543,38 +744,38 @@ t_dbg_parse_script(Arena *arena, String8 file_path, String8 source, T_DbgScript
|
|||||||
}
|
}
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
// @test:
|
// @test:
|
||||||
{
|
{
|
||||||
// first child node of the root must be a test header
|
// first child node of the root must be a test header
|
||||||
MD_Node *test = script_parse.root->first;
|
MD_Node *test = script_parse.root->first;
|
||||||
|
|
||||||
// is node test? -> error
|
// is node test? -> error
|
||||||
if ( ! str8_matchi(test->string, str8_lit("test"))) {
|
if ( ! str8_matchi(test->string, str8_lit("test"))) {
|
||||||
t_errorf("ERROR: %S: missing test header\n", file_path);
|
t_errorf("ERROR: %S: missing test header\n", file_path);
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
for MD_EachNode(n, test->first) {
|
for MD_EachNode(n, test->first) {
|
||||||
OperatingSystem os = operating_system_from_string(n->string);
|
OperatingSystem os = operating_system_from_string(n->string);
|
||||||
|
|
||||||
// is OS string correct? -> error
|
// is OS string correct? -> error
|
||||||
if (os == OperatingSystem_Null && n->string.size > 0) {
|
if (os == OperatingSystem_Null && n->string.size > 0) {
|
||||||
t_errorf_md(file_path, source, n, "test is defined for unknown os: \"%S\"\n", n->string);
|
t_errorf_md(file_path, source, n, "test is defined for unknown os: \"%S\"\n", n->string);
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
for MD_EachNode(field, n->first) {
|
for MD_EachNode(field, n->first) {
|
||||||
// define table for mapping string to a directive kind
|
// define table for mapping string to a directive kind
|
||||||
struct {
|
struct {
|
||||||
T_DbgScriptDirectiveKind kind;
|
T_DbgScriptDirectiveKind kind;
|
||||||
String8 string;
|
String8 string;
|
||||||
} dir_string_map[] = {
|
} dir_string_map[] = {
|
||||||
#define X(q,w) { T_DbgScriptDirectiveKind_##q, str8_lit(w) },
|
#define X(q,w) { T_DbgScriptDirectiveKind_##q, str8_lit(w) },
|
||||||
T_DbgScriptDirectiveKind_XList
|
T_DbgScriptDirectiveKind_XList
|
||||||
#undef X
|
#undef X
|
||||||
};
|
};
|
||||||
|
|
||||||
// string -> directive
|
// string -> directive
|
||||||
T_DbgScriptDirectiveKind kind = T_DbgScriptDirectiveKind_Null;
|
T_DbgScriptDirectiveKind kind = T_DbgScriptDirectiveKind_Null;
|
||||||
for EachElement(i, dir_string_map) {
|
for EachElement(i, dir_string_map) {
|
||||||
@@ -583,18 +784,18 @@ t_dbg_parse_script(Arena *arena, String8 file_path, String8 source, T_DbgScript
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// was directive found? -> error
|
// was directive found? -> error
|
||||||
if (kind == T_DbgScriptDirectiveKind_Null) {
|
if (kind == T_DbgScriptDirectiveKind_Null) {
|
||||||
t_errorf_md(file_path, source, n, "unknown field in test header \"%S\"\n", field->string);
|
t_errorf_md(file_path, source, n, "unknown field in test header \"%S\"\n", field->string);
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
// alloc directive node
|
// alloc directive node
|
||||||
T_DbgScriptDirective *dir = push_array(arena, T_DbgScriptDirective, 1);
|
T_DbgScriptDirective *dir = push_array(arena, T_DbgScriptDirective, 1);
|
||||||
dir->kind = kind;
|
dir->kind = kind;
|
||||||
dir->pt = mg_txt_pt_from_string_off(source, field->src_offset);
|
dir->pt = mg_txt_pt_from_string_off(source, field->src_offset);
|
||||||
|
|
||||||
// TODO: collapse
|
// TODO: collapse
|
||||||
if (field->flags & MD_NodeFlag_HasBraceLeft) {
|
if (field->flags & MD_NodeFlag_HasBraceLeft) {
|
||||||
// walk each sub-field of the MD node, and find nodes for the directive
|
// walk each sub-field of the MD node, and find nodes for the directive
|
||||||
@@ -607,9 +808,9 @@ t_dbg_parse_script(Arena *arena, String8 file_path, String8 source, T_DbgScript
|
|||||||
t_errorf_md(file_path, source, sub_field, "value of CC must be a string literal e.g. CC: \"clang\"\n");
|
t_errorf_md(file_path, source, sub_field, "value of CC must be a string literal e.g. CC: \"clang\"\n");
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (str8_matchi(cc->string, str8_lit("clang"))) { dir->compile.cc = T_Compiler_Clang; }
|
if (str8_matchi(cc->string, str8_lit("clang"))) { dir->compile.cc = Compiler_clang; }
|
||||||
else if (str8_matchi(cc->string, str8_lit("cl"))) { dir->compile.cc = T_Compiler_Cl; }
|
else if (str8_matchi(cc->string, str8_lit("cl"))) { dir->compile.cc = Compiler_msvc; }
|
||||||
else {
|
else {
|
||||||
t_errorf_md(file_path, source, cc, "unknown compiler name: \"%S\"\n", sub_field->string);
|
t_errorf_md(file_path, source, cc, "unknown compiler name: \"%S\"\n", sub_field->string);
|
||||||
goto exit;
|
goto exit;
|
||||||
@@ -639,23 +840,23 @@ t_dbg_parse_script(Arena *arena, String8 file_path, String8 source, T_DbgScript
|
|||||||
t_errorf_md(file_path, source, field, "missing value on field %S\n", field->string);
|
t_errorf_md(file_path, source, field, "missing value on field %S\n", field->string);
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
// more than one node? -> error
|
// more than one node? -> error
|
||||||
if ( ! md_node_is_nil(field->first->next)) {
|
if ( ! md_node_is_nil(field->first->next)) {
|
||||||
t_errorf_md(file_path, source, field, "field %S accepts only one value\n", field->string);
|
t_errorf_md(file_path, source, field, "field %S accepts only one value\n", field->string);
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
// node is not a string literal? -> error
|
// node is not a string literal? -> error
|
||||||
if (~field->first->flags & MD_NodeFlag_StringLiteral) {
|
if (~field->first->flags & MD_NodeFlag_StringLiteral) {
|
||||||
t_errorf_md(file_path, source, field, "field %S accepts only strings\n", field->string);
|
t_errorf_md(file_path, source, field, "field %S accepts only strings\n", field->string);
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
// copy string from MD node
|
// copy string from MD node
|
||||||
dir->args = str8_copy(arena, field->first->string);
|
dir->args = str8_copy(arena, field->first->string);
|
||||||
}
|
}
|
||||||
|
|
||||||
// append new directive
|
// append new directive
|
||||||
T_DbgScriptDirectiveList *list = &script.directives[os][kind];
|
T_DbgScriptDirectiveList *list = &script.directives[os][kind];
|
||||||
SLLQueuePush(list->first, list->last, dir);
|
SLLQueuePush(list->first, list->last, dir);
|
||||||
@@ -670,18 +871,18 @@ t_dbg_parse_script(Arena *arena, String8 file_path, String8 source, T_DbgScript
|
|||||||
MD_NodePtrList files = {0};
|
MD_NodePtrList files = {0};
|
||||||
for MD_EachNode(n, script_parse.root->first->next) {
|
for MD_EachNode(n, script_parse.root->first->next) {
|
||||||
if (str8_matchi(n->string, str8_lit("file"))) {
|
if (str8_matchi(n->string, str8_lit("file"))) {
|
||||||
|
|
||||||
// is file value a string? -> error
|
// is file value a string? -> error
|
||||||
if ( ! md_node_is_nil(n->first->next) || ! (n->first->flags & MD_NodeFlag_StringLiteral)) {
|
if ( ! md_node_is_nil(n->first->next) || ! (n->first->flags & MD_NodeFlag_StringLiteral)) {
|
||||||
t_errorf_md(file_path, source, n, "value of the 'file' must be a string, (e.g. file: \"main.c\")\n");
|
t_errorf_md(file_path, source, n, "value of the 'file' must be a string, (e.g. file: \"main.c\")\n");
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
// append file node
|
// append file node
|
||||||
md_node_ptr_list_push(scratch.arena, &files, n);
|
md_node_ptr_list_push(scratch.arena, &files, n);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// no nodes? -> treat whole file as a script
|
// no nodes? -> treat whole file as a script
|
||||||
if (files.count == 0) {
|
if (files.count == 0) {
|
||||||
MD_Node *whole_file = push_array(scratch.arena, MD_Node, 1);
|
MD_Node *whole_file = push_array(scratch.arena, MD_Node, 1);
|
||||||
@@ -691,14 +892,14 @@ t_dbg_parse_script(Arena *arena, String8 file_path, String8 source, T_DbgScript
|
|||||||
whole_file->first->string = str8_skip_last_slash(file_path);
|
whole_file->first->string = str8_skip_last_slash(file_path);
|
||||||
md_node_ptr_list_push(scratch.arena, &files, whole_file);
|
md_node_ptr_list_push(scratch.arena, &files, whole_file);
|
||||||
}
|
}
|
||||||
|
|
||||||
HashMap files_hm = {0};
|
HashMap files_hm = {0};
|
||||||
for EachNode(n_ptr, MD_NodePtrNode, files.first) {
|
for EachNode(n_ptr, MD_NodePtrNode, files.first) {
|
||||||
MD_Node *n = n_ptr->v;
|
MD_Node *n = n_ptr->v;
|
||||||
|
|
||||||
// get file name
|
// get file name
|
||||||
String8 file_name = n->first->string;
|
String8 file_name = n->first->string;
|
||||||
|
|
||||||
// was file seen? -> error
|
// was file seen? -> error
|
||||||
MD_Node *is_declared = hash_map_search_string_raw(&files_hm, file_name);
|
MD_Node *is_declared = hash_map_search_string_raw(&files_hm, file_name);
|
||||||
if (is_declared) {
|
if (is_declared) {
|
||||||
@@ -707,19 +908,19 @@ t_dbg_parse_script(Arena *arena, String8 file_path, String8 source, T_DbgScript
|
|||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
hash_map_push_string_raw(scratch.arena, &files_hm, file_name, n);
|
hash_map_push_string_raw(scratch.arena, &files_hm, file_name, n);
|
||||||
|
|
||||||
// file ends at EOF or before the next file directive
|
// file ends at EOF or before the next file directive
|
||||||
U64 src_opl = source.size;
|
U64 src_opl = source.size;
|
||||||
if (n_ptr->next) {
|
if (n_ptr->next) {
|
||||||
src_opl = n_ptr->next->v->src_offset;
|
src_opl = n_ptr->next->v->src_offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
// sub-string the source file
|
// sub-string the source file
|
||||||
String8 sub_source = str8_substr(source, r1u64(n->src_offset, src_opl));
|
String8 sub_source = str8_substr(source, r1u64(n->src_offset, src_opl));
|
||||||
U64 file_min = str8_find_needle(sub_source, 0, str8_lit("\n"), 0) + 1;
|
U64 file_min = str8_find_needle(sub_source, 0, str8_lit("\n"), 0) + 1;
|
||||||
U64 file_max = str8_find_needle_reverse(sub_source, 0, str8_lit("\n"), 0);
|
U64 file_max = str8_find_needle_reverse(sub_source, 0, str8_lit("\n"), 0);
|
||||||
sub_source = str8_substr(sub_source, r1u64(file_min, file_max));
|
sub_source = str8_substr(sub_source, r1u64(file_min, file_max));
|
||||||
|
|
||||||
// append new file node
|
// append new file node
|
||||||
T_DbgScriptFile *file = push_array(arena, T_DbgScriptFile, 1);
|
T_DbgScriptFile *file = push_array(arena, T_DbgScriptFile, 1);
|
||||||
file->path = t_make_file_path(arena, file_name);
|
file->path = t_make_file_path(arena, file_name);
|
||||||
@@ -737,9 +938,9 @@ t_dbg_parse_script(Arena *arena, String8 file_path, String8 source, T_DbgScript
|
|||||||
// find order number nodes
|
// find order number nodes
|
||||||
U64 order = 0;
|
U64 order = 0;
|
||||||
if ( ! try_u64_from_str8_c_rules(n->string, &order)) {
|
if ( ! try_u64_from_str8_c_rules(n->string, &order)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// correlate MD node to the script file
|
// correlate MD node to the script file
|
||||||
T_DbgScriptFile *file = 0;
|
T_DbgScriptFile *file = 0;
|
||||||
for (file = script.files.first; file != 0; file = file->next) {
|
for (file = script.files.first; file != 0; file = file->next) {
|
||||||
@@ -747,7 +948,7 @@ t_dbg_parse_script(Arena *arena, String8 file_path, String8 source, T_DbgScript
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// found file? -> error
|
// found file? -> error
|
||||||
if (file == 0) {
|
if (file == 0) {
|
||||||
t_errorf_md(file_path, source, n, "failed to correlate MD node to the script source file\n");
|
t_errorf_md(file_path, source, n, "failed to correlate MD node to the script source file\n");
|
||||||
@@ -760,7 +961,7 @@ t_dbg_parse_script(Arena *arena, String8 file_path, String8 source, T_DbgScript
|
|||||||
t_errorf_md(file_path, source, n, "duplicate order number %llu found, previous defined at %llu\n", order, p->pt.line);
|
t_errorf_md(file_path, source, n, "duplicate order number %llu found, previous defined at %llu\n", order, p->pt.line);
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
// alloc & fill out program
|
// alloc & fill out program
|
||||||
p = push_array(arena, T_DbgScriptProgram, 1);
|
p = push_array(arena, T_DbgScriptProgram, 1);
|
||||||
p->pt = mg_txt_pt_from_string_off(source, n->src_offset);
|
p->pt = mg_txt_pt_from_string_off(source, n->src_offset);
|
||||||
@@ -779,9 +980,9 @@ t_dbg_parse_script(Arena *arena, String8 file_path, String8 source, T_DbgScript
|
|||||||
|
|
||||||
// parse cmd args
|
// parse cmd args
|
||||||
MD_Node *cmd_arg = cmd_n->first;
|
MD_Node *cmd_arg = cmd_n->first;
|
||||||
|
|
||||||
if (md_node_is_nil(cmd_arg)) { continue; }
|
if (md_node_is_nil(cmd_arg)) { continue; }
|
||||||
|
|
||||||
if (cmd->kind == T_DbgScriptCmdKind_At) {
|
if (cmd->kind == T_DbgScriptCmdKind_At) {
|
||||||
if ( ! try_s64_from_str8_c_rules(cmd_arg->string, &cmd->at_delta)) {
|
if ( ! try_s64_from_str8_c_rules(cmd_arg->string, &cmd->at_delta)) {
|
||||||
t_errorf_md(file_path, source, cmd_arg, "failed to parse \"%S\"", cmd_arg->string);
|
t_errorf_md(file_path, source, cmd_arg, "failed to parse \"%S\"", cmd_arg->string);
|
||||||
@@ -868,7 +1069,7 @@ t_dbg_script_invoke(T_DbgScript *script, U64 timeout_us)
|
|||||||
S64 at_line_s64 = (S64)(program->pt.line - program->file->pt.line) + cmd->at_delta;
|
S64 at_line_s64 = (S64)(program->pt.line - program->file->pt.line) + cmd->at_delta;
|
||||||
U64 at_line_u64 = at_line_s64 >= 0 ? (U64)at_line_s64 : 0;
|
U64 at_line_u64 = at_line_s64 >= 0 ? (U64)at_line_s64 : 0;
|
||||||
AssertAlways(at_line_u64 > 0);
|
AssertAlways(at_line_u64 > 0);
|
||||||
|
|
||||||
if (lines.count == 0) {
|
if (lines.count == 0) {
|
||||||
t_errorf("ERROR: %S:%llu:%llu: no source location maps for vaddr: 0x%llx\n", script->file_path, cmd->pt.line, cmd->pt.column, ip);
|
t_errorf("ERROR: %S:%llu:%llu: no source location maps for vaddr: 0x%llx\n", script->file_path, cmd->pt.line, cmd->pt.column, ip);
|
||||||
goto exit;
|
goto exit;
|
||||||
@@ -877,7 +1078,7 @@ t_dbg_script_invoke(T_DbgScript *script, U64 timeout_us)
|
|||||||
// match expected vs current debugger locations
|
// match expected vs current debugger locations
|
||||||
for EachIndex(i, lines.count) {
|
for EachIndex(i, lines.count) {
|
||||||
B32 mismatch = lines.v[i].pt.line != at_line_u64 ||
|
B32 mismatch = lines.v[i].pt.line != at_line_u64 ||
|
||||||
!str8_match(lines.v[i].file_path, program->file->path, StringMatchFlag_CaseInsensitive|StringMatchFlag_SlashInsensitive);
|
!str8_match(lines.v[i].file_path, program->file->path, StringMatchFlag_CaseInsensitive|StringMatchFlag_SlashInsensitive);
|
||||||
if (mismatch) {
|
if (mismatch) {
|
||||||
t_errorf("ERROR: %S:%llu: location check did not pass:\n", script->file_path, cmd->pt.line);
|
t_errorf("ERROR: %S:%llu: location check did not pass:\n", script->file_path, cmd->pt.line);
|
||||||
t_errorf(" Expected: %S:%llu\n", program->file->path, at_line_u64);
|
t_errorf(" Expected: %S:%llu\n", program->file->path, at_line_u64);
|
||||||
@@ -914,7 +1115,7 @@ T_RunSig(dbg_script_runner)
|
|||||||
// source -> script
|
// source -> script
|
||||||
T_DbgScript script = {0};
|
T_DbgScript script = {0};
|
||||||
if ( ! t_dbg_parse_script(arena, user_data, source, &script)) {
|
if ( ! t_dbg_parse_script(arena, user_data, source, &script)) {
|
||||||
result_out->status = T_RunStatus_Fail;
|
result_out->status = TestStatus_Fail;
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -925,7 +1126,7 @@ T_RunSig(dbg_script_runner)
|
|||||||
T_Ok(0);
|
T_Ok(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// compiler vars
|
// compiler vars
|
||||||
HashTable *script_vars = hash_table_init(arena, 1000);
|
HashTable *script_vars = hash_table_init(arena, 1000);
|
||||||
hash_table_push_path_string(arena, script_vars, str8_lit("FILE"), user_data);
|
hash_table_push_path_string(arena, script_vars, str8_lit("FILE"), user_data);
|
||||||
@@ -934,37 +1135,37 @@ T_RunSig(dbg_script_runner)
|
|||||||
|
|
||||||
// run compilers
|
// run compilers
|
||||||
for EachNode(directive, T_DbgScriptDirective, script.directives[OperatingSystem_CURRENT][T_DbgScriptDirectiveKind_Compile].first) {
|
for EachNode(directive, T_DbgScriptDirective, script.directives[OperatingSystem_CURRENT][T_DbgScriptDirectiveKind_Compile].first) {
|
||||||
T_Compiler compiler = directive->compile.cc;
|
Compiler compiler = directive->compile.cc;
|
||||||
|
|
||||||
// pick default compiler if none selected
|
// pick default compiler if none selected
|
||||||
if (directive->compile.cc == T_Compiler_Null) {
|
if (directive->compile.cc == Compiler_Null) {
|
||||||
switch (OperatingSystem_CURRENT) {
|
switch (OperatingSystem_CURRENT) {
|
||||||
case OperatingSystem_Windows: { compiler = T_Compiler_Cl; } break;
|
case OperatingSystem_Windows: { compiler = Compiler_msvc; } break;
|
||||||
case OperatingSystem_Linux: { compiler = T_Compiler_Clang; } break;
|
case OperatingSystem_Linux: { compiler = Compiler_clang; } break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// get compiler path
|
// get compiler path
|
||||||
String8 compiler_path = {0};
|
String8 compiler_path = {0};
|
||||||
switch (compiler) {
|
switch (compiler) {
|
||||||
case T_Compiler_Null: break;
|
case Compiler_Null: break;
|
||||||
case T_Compiler_Cl: compiler_path = t_cl_path(); break;
|
case Compiler_msvc: compiler_path = t_cl_path(); break;
|
||||||
case T_Compiler_Clang: compiler_path = t_clang_path(); break;
|
case Compiler_clang: compiler_path = t_clang_path(); break;
|
||||||
case T_Compiler_Gcc: compiler_path = t_gcc_path(); break;
|
case Compiler_gcc: compiler_path = t_gcc_path(); break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// invoke compiler with arguments from directive
|
// invoke compiler with arguments from directive
|
||||||
String8 expanded_args = lnk_expand_env_vars_windows(arena, script_vars, directive->args);
|
String8 expanded_args = lnk_expand_env_vars_windows(arena, script_vars, directive->args);
|
||||||
|
|
||||||
if (compiler == T_Compiler_Cl) { expanded_args = str8f(arena, "/nologo %S", expanded_args); }
|
if (compiler == Compiler_msvc) { expanded_args = str8f(arena, "/nologo %S", expanded_args); }
|
||||||
|
|
||||||
if (t_invoke(compiler_path, expanded_args, max_U64) == 0) {
|
if (t_invoke(compiler_path, expanded_args, max_U64) == 0) {
|
||||||
t_errorf("ERROR: failed to launch compiler: \"%S %S\"\n", compiler_path, expanded_args);
|
t_errorf("ERROR: failed to launch compiler: \"%S %S\"\n", compiler_path, expanded_args);
|
||||||
T_Ok(0);
|
T_Ok(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (compiler == T_Compiler_Cl) { g_output = str8_skip(g_output, str8_chop_line(&g_output).size); } // file name print
|
if (compiler == Compiler_msvc) { g_output = str8_skip(g_output, str8_chop_line(&g_output).size); } // file name print
|
||||||
|
|
||||||
if (g_last_exit_code) {
|
if (g_last_exit_code) {
|
||||||
t_errorf("ERROR: %S:%llu: %S\n", script.file_path, (unsigned long long)directive->pt.line, g_errors);
|
t_errorf("ERROR: %S:%llu: %S\n", script.file_path, (unsigned long long)directive->pt.line, g_errors);
|
||||||
if (g_stop_on_first_fail_or_crash) {
|
if (g_stop_on_first_fail_or_crash) {
|
||||||
@@ -986,19 +1187,13 @@ T_RunSig(dbg_script_runner)
|
|||||||
T_Ok(0);
|
T_Ok(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// if test does not have any directive -> skip
|
|
||||||
U64 total_directive_count = 0;
|
|
||||||
for EachIndex(i, ArrayCount(script.directives[OperatingSystem_CURRENT])) {
|
|
||||||
total_directive_count += script.directives[OperatingSystem_CURRENT][i].count;
|
|
||||||
}
|
|
||||||
|
|
||||||
// is skip flag set? -> exit
|
// is skip flag set? -> exit
|
||||||
if (g_build_only || total_directive_count == 0 || script.directives[OperatingSystem_CURRENT][T_DbgScriptDirectiveKind_Skip].count) {
|
if (g_build_only || script.directives[OperatingSystem_CURRENT][T_DbgScriptDirectiveKind_Skip].count) {
|
||||||
result_out->status = T_RunStatus_Skip;
|
result_out->status = TestStatus_Skip;
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
// launch targets
|
// launch targets
|
||||||
for EachNode(directive, T_DbgScriptDirective, script.directives[OperatingSystem_CURRENT][T_DbgScriptDirectiveKind_Launch].first) {
|
for EachNode(directive, T_DbgScriptDirective, script.directives[OperatingSystem_CURRENT][T_DbgScriptDirectiveKind_Launch].first) {
|
||||||
String8 expanded_args = lnk_expand_env_vars_windows(arena, script_vars, directive->args);
|
String8 expanded_args = lnk_expand_env_vars_windows(arena, script_vars, directive->args);
|
||||||
+138
-150
@@ -72,31 +72,6 @@ t_test_group_from_name(Arena *arena, String8 pattern)
|
|||||||
|
|
||||||
////////////////////////////////
|
////////////////////////////////
|
||||||
|
|
||||||
internal char *
|
|
||||||
t_string_from_result(T_RunStatus v)
|
|
||||||
{
|
|
||||||
switch (v) {
|
|
||||||
case T_RunStatus_Fail: return "FAIL";
|
|
||||||
case T_RunStatus_Crash: return "CRASH";
|
|
||||||
case T_RunStatus_Pass: return "PASS";
|
|
||||||
case T_RunStatus_Skip: return "SKIP";
|
|
||||||
default: break;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal char *
|
|
||||||
t_color_from_result(T_RunStatus v)
|
|
||||||
{
|
|
||||||
switch (v) {
|
|
||||||
#define X(n,c,...) case T_RunStatus_##n: return c;
|
|
||||||
T_Run_XList
|
|
||||||
#undef X
|
|
||||||
default: break;
|
|
||||||
}
|
|
||||||
return "null";
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void
|
internal void
|
||||||
t_break_if_debugger_present(void)
|
t_break_if_debugger_present(void)
|
||||||
{
|
{
|
||||||
@@ -108,14 +83,14 @@ t_break_if_debugger_present(void)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
internal T_Linker
|
internal Linker
|
||||||
t_id_linker(void)
|
t_id_linker(void)
|
||||||
{
|
{
|
||||||
String8 name = str8_chop_last_dot(str8_skip_last_slash(g_linker_path));
|
String8 name = str8_chop_last_dot(str8_skip_last_slash(g_linker_path));
|
||||||
if (str8_match(name, str8_lit("radlink"), StringMatchFlag_CaseInsensitive)) { return T_Linker_RAD; }
|
if (str8_match(name, str8_lit("radlink"), StringMatchFlag_CaseInsensitive)) { return Linker_radlink; }
|
||||||
if (str8_match(name, str8_lit("link"), StringMatchFlag_CaseInsensitive)) { return T_Linker_MSVC; }
|
if (str8_match(name, str8_lit("link"), StringMatchFlag_CaseInsensitive)) { return Linker_msvc; }
|
||||||
if (str8_match(name, str8_lit("lld-link"), StringMatchFlag_CaseInsensitive)) { return T_Linker_LLVM; }
|
if (str8_match(name, str8_lit("lld-link"), StringMatchFlag_CaseInsensitive)) { return Linker_lld; }
|
||||||
return T_Linker_Null;
|
return Linker_Null;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal B32
|
internal B32
|
||||||
@@ -212,19 +187,19 @@ t_run_caller(void *raw_ctx)
|
|||||||
Temp scratch = scratch_begin(0,0);
|
Temp scratch = scratch_begin(0,0);
|
||||||
|
|
||||||
g_is_first_print = 1;
|
g_is_first_print = 1;
|
||||||
|
|
||||||
T_RunCtx *ctx = raw_ctx;
|
T_RunCtx *ctx = raw_ctx;
|
||||||
ctx->result.status = T_RunStatus_Pass;
|
ctx->result.status = TestStatus_Pass;
|
||||||
|
|
||||||
String8List test_out = {0};
|
String8List test_out = {0};
|
||||||
|
|
||||||
if (ctx->test->skip) {
|
if (ctx->test->skip) {
|
||||||
ctx->result.status = T_RunStatus_Skip;
|
ctx->result.status = TestStatus_Skip;
|
||||||
} else {
|
} else {
|
||||||
ctx->test->r(scratch.arena, ctx->user_data, &ctx->result, &test_out);
|
ctx->test->r(scratch.arena, ctx->user_data, &ctx->result, &test_out);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ctx->result.status == T_RunStatus_Fail || ctx->result.status == T_RunStatus_Crash) {
|
if (ctx->result.status == TestStatus_Fail || ctx->result.status == TestStatus_Crash) {
|
||||||
for EachNode(n, String8Node, test_out.first) {
|
for EachNode(n, String8Node, test_out.first) {
|
||||||
t_errorf("%S", n->string);
|
t_errorf("%S", n->string);
|
||||||
}
|
}
|
||||||
@@ -235,27 +210,27 @@ t_run_caller(void *raw_ctx)
|
|||||||
t_errorf("%S\n", g_output);
|
t_errorf("%S\n", g_output);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
scratch_end(scratch);
|
scratch_end(scratch);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal T_RunResult
|
internal TestResult
|
||||||
t_run(T_Test *test, String8 user_data)
|
t_run(T_Test *test, String8 user_data)
|
||||||
{
|
{
|
||||||
T_RunCtx ctx = { .test = test, .user_data = user_data, .result.status = T_RunStatus_Fail };
|
T_RunCtx ctx = { .test = test, .user_data = user_data, .result.status = TestStatus_Fail };
|
||||||
t_run_caller(&ctx);
|
t_run_caller(&ctx);
|
||||||
|
|
||||||
if (ctx.result.status == T_RunStatus_Fail || ctx.result.status == T_RunStatus_Crash) {
|
if (ctx.result.status == TestStatus_Fail || ctx.result.status == TestStatus_Crash) {
|
||||||
if (g_output.size > 0 || g_errors.size > 0) {
|
if (g_output.size > 0 || g_errors.size > 0) {
|
||||||
t_errorf("Last captured output:\n");
|
t_errorf("Last captured output:\n");
|
||||||
if (g_output.size) { t_errorf("%S\n", g_output); }
|
if (g_output.size) { t_errorf("%S\n", g_output); }
|
||||||
if (g_errors.size) { t_errorf("%S\n", g_errors); }
|
if (g_errors.size) { t_errorf("%S\n", g_errors); }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
fflush(stderr);
|
fflush(stderr);
|
||||||
|
|
||||||
return ctx.result;
|
return ctx.result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -336,7 +311,7 @@ t_cl_version(void)
|
|||||||
|
|
||||||
if ( ! version.size) {
|
if ( ! version.size) {
|
||||||
Temp scratch = scratch_begin(0, 0);
|
Temp scratch = scratch_begin(0, 0);
|
||||||
|
|
||||||
t_invoke_cl("");
|
t_invoke_cl("");
|
||||||
AssertAlways(g_last_exit_code == 0);
|
AssertAlways(g_last_exit_code == 0);
|
||||||
|
|
||||||
@@ -344,10 +319,10 @@ t_cl_version(void)
|
|||||||
U64 version_lo = str8_find_needle(g_output, 0, needle, 0);
|
U64 version_lo = str8_find_needle(g_output, 0, needle, 0);
|
||||||
version_lo += needle.size + 1;
|
version_lo += needle.size + 1;
|
||||||
AssertAlways(version_lo < g_output.size);
|
AssertAlways(version_lo < g_output.size);
|
||||||
|
|
||||||
U64 version_hi = str8_find_needle(g_output, version_lo, str8_lit(" "), 0);
|
U64 version_hi = str8_find_needle(g_output, version_lo, str8_lit(" "), 0);
|
||||||
AssertAlways(version_hi < g_output.size);
|
AssertAlways(version_hi < g_output.size);
|
||||||
|
|
||||||
version = str8_substr(g_output, r1u64(version_lo, version_hi));
|
version = str8_substr(g_output, r1u64(version_lo, version_hi));
|
||||||
AssertAlways(version.size > 0);
|
AssertAlways(version.size > 0);
|
||||||
|
|
||||||
@@ -355,7 +330,7 @@ t_cl_version(void)
|
|||||||
ArenaParams params = { .reserve_size = sizeof(buffer), .commit_size = sizeof(buffer), .optional_backing_buffer = buffer };
|
ArenaParams params = { .reserve_size = sizeof(buffer), .commit_size = sizeof(buffer), .optional_backing_buffer = buffer };
|
||||||
Arena *arena = arena_alloc_(¶ms);
|
Arena *arena = arena_alloc_(¶ms);
|
||||||
version = str8_copy(arena, version);
|
version = str8_copy(arena, version);
|
||||||
|
|
||||||
MemoryZeroStruct(&g_output);
|
MemoryZeroStruct(&g_output);
|
||||||
scratch_end(scratch);
|
scratch_end(scratch);
|
||||||
}
|
}
|
||||||
@@ -413,7 +388,7 @@ t_cwd_path(void)
|
|||||||
if (path[0] == 0) {
|
if (path[0] == 0) {
|
||||||
Temp scratch = scratch_begin(0, 0);
|
Temp scratch = scratch_begin(0, 0);
|
||||||
String8 cwd = get_current_path(scratch.arena);
|
String8 cwd = get_current_path(scratch.arena);
|
||||||
|
|
||||||
// TODO: linux and windows return two different things, we need to settle what to do here
|
// TODO: linux and windows return two different things, we need to settle what to do here
|
||||||
// should get_current_path return directory paths with slash or no slash
|
// should get_current_path return directory paths with slash or no slash
|
||||||
if ((str8_match_wildcard(cwd, str8_lit("*\\"), 0) && str8_match_wildcard(cwd, str8_lit("*/"), 0))) {
|
if ((str8_match_wildcard(cwd, str8_lit("*\\"), 0) && str8_match_wildcard(cwd, str8_lit("*/"), 0))) {
|
||||||
@@ -422,7 +397,7 @@ t_cwd_path(void)
|
|||||||
} else {
|
} else {
|
||||||
cwd = str8_chop_last_slash(cwd);
|
cwd = str8_chop_last_slash(cwd);
|
||||||
}
|
}
|
||||||
|
|
||||||
MemoryCopyStr8(path, cwd);
|
MemoryCopyStr8(path, cwd);
|
||||||
path[cwd.size] = 0;
|
path[cwd.size] = 0;
|
||||||
scratch_end(scratch);
|
scratch_end(scratch);
|
||||||
@@ -448,19 +423,19 @@ internal B32
|
|||||||
t_invoke_env(String8 exe_path, String8 cmdline, String8List env, U64 timeout_us)
|
t_invoke_env(String8 exe_path, String8 cmdline, String8List env, U64 timeout_us)
|
||||||
{
|
{
|
||||||
Temp scratch = scratch_begin(&g_output_arena,1);
|
Temp scratch = scratch_begin(&g_output_arena,1);
|
||||||
|
|
||||||
B32 is_ok = 0;
|
B32 is_ok = 0;
|
||||||
|
|
||||||
// clean up global state
|
// clean up global state
|
||||||
arena_clear(g_output_arena);
|
arena_clear(g_output_arena);
|
||||||
MemoryZeroStruct(&g_output);
|
MemoryZeroStruct(&g_output);
|
||||||
g_last_exit_code = max_U64;
|
g_last_exit_code = max_U64;
|
||||||
|
|
||||||
String8List stdout_parts = {0};
|
String8List stdout_parts = {0};
|
||||||
String8List stderr_parts = {0};
|
String8List stderr_parts = {0};
|
||||||
U64 stdout_idx = 0;
|
U64 stdout_idx = 0;
|
||||||
U64 stderr_idx = 1;
|
U64 stderr_idx = 1;
|
||||||
|
|
||||||
#if OS_WINDOWS
|
#if OS_WINDOWS
|
||||||
typedef enum {
|
typedef enum {
|
||||||
Win32CaptureState_Null,
|
Win32CaptureState_Null,
|
||||||
@@ -478,7 +453,7 @@ t_invoke_env(String8 exe_path, String8 cmdline, String8List env, U64 timeout_us)
|
|||||||
String8List *parts;
|
String8List *parts;
|
||||||
Win32CaptureState state;
|
Win32CaptureState state;
|
||||||
} Win32Capture;
|
} Win32Capture;
|
||||||
|
|
||||||
Win32Capture captures_win32[2] = {0};
|
Win32Capture captures_win32[2] = {0};
|
||||||
for EachElement(i, captures_win32) {
|
for EachElement(i, captures_win32) {
|
||||||
// create read pipe
|
// create read pipe
|
||||||
@@ -488,23 +463,23 @@ t_invoke_env(String8 exe_path, String8 cmdline, String8List env, U64 timeout_us)
|
|||||||
SECURITY_ATTRIBUTES read_at = { .nLength = sizeof(read_at), .bInheritHandle = 0 };
|
SECURITY_ATTRIBUTES read_at = { .nLength = sizeof(read_at), .bInheritHandle = 0 };
|
||||||
captures_win32[i].read_pipe_handle = CreateNamedPipeW(pipe_name16.str, PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED, PIPE_TYPE_BYTE | PIPE_WAIT, 1, MB(1), MB(1), 0, &read_at);
|
captures_win32[i].read_pipe_handle = CreateNamedPipeW(pipe_name16.str, PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED, PIPE_TYPE_BYTE | PIPE_WAIT, 1, MB(1), MB(1), 0, &read_at);
|
||||||
AssertAlways(captures_win32[i].read_pipe_handle != INVALID_HANDLE_VALUE);
|
AssertAlways(captures_win32[i].read_pipe_handle != INVALID_HANDLE_VALUE);
|
||||||
|
|
||||||
// create overlapped write file
|
// create overlapped write file
|
||||||
SECURITY_ATTRIBUTES write_at = { .nLength = sizeof(write_at), .bInheritHandle = 1 };
|
SECURITY_ATTRIBUTES write_at = { .nLength = sizeof(write_at), .bInheritHandle = 1 };
|
||||||
captures_win32[i].write_pipe_handle = CreateFileW(pipe_name16.str, GENERIC_WRITE, 0, &write_at, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
|
captures_win32[i].write_pipe_handle = CreateFileW(pipe_name16.str, GENERIC_WRITE, 0, &write_at, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
|
||||||
AssertAlways(captures_win32[i].write_pipe_handle != INVALID_HANDLE_VALUE);
|
AssertAlways(captures_win32[i].write_pipe_handle != INVALID_HANDLE_VALUE);
|
||||||
|
|
||||||
// create event for overlapped
|
// create event for overlapped
|
||||||
captures_win32[i].event = CreateEventW(0, 1, 0, 0);
|
captures_win32[i].event = CreateEventW(0, 1, 0, 0);
|
||||||
AssertAlways(captures_win32[i].event != NULL);
|
AssertAlways(captures_win32[i].event != NULL);
|
||||||
|
|
||||||
// alloc capture buffer
|
// alloc capture buffer
|
||||||
captures_win32[i].buffer_size = MB(1);
|
captures_win32[i].buffer_size = MB(1);
|
||||||
captures_win32[i].buffer = push_array(scratch.arena, U8, captures_win32[i].buffer_size);
|
captures_win32[i].buffer = push_array(scratch.arena, U8, captures_win32[i].buffer_size);
|
||||||
}
|
}
|
||||||
captures_win32[stdout_idx].parts = &stdout_parts;
|
captures_win32[stdout_idx].parts = &stdout_parts;
|
||||||
captures_win32[stderr_idx].parts = &stderr_parts;
|
captures_win32[stderr_idx].parts = &stderr_parts;
|
||||||
|
|
||||||
File read_capture_handles [ArrayCount(captures_win32)] = {0};
|
File read_capture_handles [ArrayCount(captures_win32)] = {0};
|
||||||
File write_capture_handles[ArrayCount(captures_win32)] = {0};
|
File write_capture_handles[ArrayCount(captures_win32)] = {0};
|
||||||
for EachElement(i, captures_win32) { read_capture_handles[i] = (File){ (U64)captures_win32[i].read_pipe_handle }; }
|
for EachElement(i, captures_win32) { read_capture_handles[i] = (File){ (U64)captures_win32[i].read_pipe_handle }; }
|
||||||
@@ -516,7 +491,7 @@ t_invoke_env(String8 exe_path, String8 cmdline, String8List env, U64 timeout_us)
|
|||||||
B32 is_live;
|
B32 is_live;
|
||||||
struct pollfd *poll_fd;
|
struct pollfd *poll_fd;
|
||||||
} LinuxCapture;
|
} LinuxCapture;
|
||||||
|
|
||||||
LinuxCapture captures_linux[2] = {0};
|
LinuxCapture captures_linux[2] = {0};
|
||||||
for EachElement(i, captures_linux) {
|
for EachElement(i, captures_linux) {
|
||||||
if (pipe2(captures_linux[i].fds, 0) != 0) {
|
if (pipe2(captures_linux[i].fds, 0) != 0) {
|
||||||
@@ -525,10 +500,10 @@ t_invoke_env(String8 exe_path, String8 cmdline, String8List env, U64 timeout_us)
|
|||||||
}
|
}
|
||||||
captures_linux[i].is_live = 1;
|
captures_linux[i].is_live = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
captures_linux[0].parts = &stdout_parts;
|
captures_linux[0].parts = &stdout_parts;
|
||||||
captures_linux[1].parts = &stderr_parts;
|
captures_linux[1].parts = &stderr_parts;
|
||||||
|
|
||||||
File read_capture_handles[2] = {0}, write_capture_handles[2] = {0};
|
File read_capture_handles[2] = {0}, write_capture_handles[2] = {0};
|
||||||
read_capture_handles[0].u64[0] = captures_linux[0].fds[0];
|
read_capture_handles[0].u64[0] = captures_linux[0].fds[0];
|
||||||
read_capture_handles[1].u64[0] = captures_linux[1].fds[0];
|
read_capture_handles[1].u64[0] = captures_linux[1].fds[0];
|
||||||
@@ -546,13 +521,13 @@ t_invoke_env(String8 exe_path, String8 cmdline, String8List env, U64 timeout_us)
|
|||||||
.cmd_line = lnk_arg_list_parse_windows_rules(scratch.arena, cmdline),
|
.cmd_line = lnk_arg_list_parse_windows_rules(scratch.arena, cmdline),
|
||||||
};
|
};
|
||||||
str8_list_push_front(scratch.arena, &launch_opts.cmd_line, exe_path);
|
str8_list_push_front(scratch.arena, &launch_opts.cmd_line, exe_path);
|
||||||
|
|
||||||
String8 full_cmd_line = str8_list_join(scratch.arena, &launch_opts.cmd_line, &(StringJoin){ .sep = str8_lit(" ") });
|
String8 full_cmd_line = str8_list_join(scratch.arena, &launch_opts.cmd_line, &(StringJoin){ .sep = str8_lit(" ") });
|
||||||
|
|
||||||
// invoke exe
|
// invoke exe
|
||||||
Process process_handle = process_launch(&launch_opts);
|
Process process_handle = process_launch(&launch_opts);
|
||||||
if (process_match(process_handle, process_zero())) { goto exit; }
|
if (process_match(process_handle, process_zero())) { goto exit; }
|
||||||
|
|
||||||
// capture process output
|
// capture process output
|
||||||
#if OS_WINDOWS
|
#if OS_WINDOWS
|
||||||
{
|
{
|
||||||
@@ -561,24 +536,24 @@ t_invoke_env(String8 exe_path, String8 cmdline, String8List env, U64 timeout_us)
|
|||||||
CloseHandle(captures_win32[i].write_pipe_handle);
|
CloseHandle(captures_win32[i].write_pipe_handle);
|
||||||
MemoryZeroStruct(&write_capture_handles[i]);
|
MemoryZeroStruct(&write_capture_handles[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
B32 is_process_live = 1;
|
B32 is_process_live = 1;
|
||||||
for (U64 endt_us = ENDT_US(timeout_us);;) {
|
for (U64 endt_us = ENDT_US(timeout_us);;) {
|
||||||
HANDLE wait_handles[ArrayCount(captures_win32) + 1] = {0};
|
HANDLE wait_handles[ArrayCount(captures_win32) + 1] = {0};
|
||||||
U64 wait_handle_count = 0;
|
U64 wait_handle_count = 0;
|
||||||
|
|
||||||
// queue process
|
// queue process
|
||||||
if (is_process_live) {
|
if (is_process_live) {
|
||||||
wait_handles[wait_handle_count++] = (HANDLE)process_handle.u64[0];
|
wait_handles[wait_handle_count++] = (HANDLE)process_handle.u64[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
for EachElement(capture_idx, captures_win32) {
|
for EachElement(capture_idx, captures_win32) {
|
||||||
while (captures_win32[capture_idx].state == Win32CaptureState_Null) {
|
while (captures_win32[capture_idx].state == Win32CaptureState_Null) {
|
||||||
// init overlapped so when child writes to capture buffer this event is signaled
|
// init overlapped so when child writes to capture buffer this event is signaled
|
||||||
AssertAlways(ResetEvent(captures_win32[capture_idx].event));
|
AssertAlways(ResetEvent(captures_win32[capture_idx].event));
|
||||||
MemoryZeroStruct(&captures_win32[capture_idx].overlapped);
|
MemoryZeroStruct(&captures_win32[capture_idx].overlapped);
|
||||||
captures_win32[capture_idx].overlapped.hEvent = captures_win32[capture_idx].event;
|
captures_win32[capture_idx].overlapped.hEvent = captures_win32[capture_idx].event;
|
||||||
|
|
||||||
// begin overlapped read
|
// begin overlapped read
|
||||||
DWORD read_size = 0;
|
DWORD read_size = 0;
|
||||||
if (ReadFile(captures_win32[capture_idx].read_pipe_handle, captures_win32[capture_idx].buffer, captures_win32[capture_idx].buffer_size, &read_size, &captures_win32[capture_idx].overlapped)) {
|
if (ReadFile(captures_win32[capture_idx].read_pipe_handle, captures_win32[capture_idx].buffer, captures_win32[capture_idx].buffer_size, &read_size, &captures_win32[capture_idx].overlapped)) {
|
||||||
@@ -600,7 +575,7 @@ t_invoke_env(String8 exe_path, String8 cmdline, String8List env, U64 timeout_us)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (captures_win32[capture_idx].state == Win32CaptureState_Pending) {
|
if (captures_win32[capture_idx].state == Win32CaptureState_Pending) {
|
||||||
// event now should signal whenever pipe has data to read
|
// event now should signal whenever pipe has data to read
|
||||||
captures_win32[capture_idx].wait_idx = wait_handle_count++;
|
captures_win32[capture_idx].wait_idx = wait_handle_count++;
|
||||||
@@ -609,23 +584,23 @@ t_invoke_env(String8 exe_path, String8 cmdline, String8List env, U64 timeout_us)
|
|||||||
captures_win32[capture_idx].wait_idx = max_U64;
|
captures_win32[capture_idx].wait_idx = max_U64;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// exit if there are no handles
|
// exit if there are no handles
|
||||||
if (wait_handle_count == 0) { break; }
|
if (wait_handle_count == 0) { break; }
|
||||||
|
|
||||||
// compute wait time
|
// compute wait time
|
||||||
DWORD wait_ms = INFINITE;
|
DWORD wait_ms = INFINITE;
|
||||||
if (timeout_us != max_U64) {
|
if (timeout_us != max_U64) {
|
||||||
U64 now_us = now_time_us();
|
U64 now_us = now_time_us();
|
||||||
wait_ms = now_us < endt_us ? ClampTop((endt_us - now_us + 999) / 1000, max_U32-1) : 0;
|
wait_ms = now_us < endt_us ? ClampTop((endt_us - now_us + 999) / 1000, max_U32-1) : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// wait on process and read pipes
|
// wait on process and read pipes
|
||||||
DWORD wait_result = WaitForMultipleObjects(wait_handle_count, wait_handles, 0, wait_ms);
|
DWORD wait_result = WaitForMultipleObjects(wait_handle_count, wait_handles, 0, wait_ms);
|
||||||
|
|
||||||
if (wait_result >= WAIT_OBJECT_0 && wait_result < WAIT_OBJECT_0 + wait_handle_count) {
|
if (wait_result >= WAIT_OBJECT_0 && wait_result < WAIT_OBJECT_0 + wait_handle_count) {
|
||||||
DWORD wait_idx = wait_result - WAIT_OBJECT_0;
|
DWORD wait_idx = wait_result - WAIT_OBJECT_0;
|
||||||
|
|
||||||
if (is_process_live && wait_idx == 0) {
|
if (is_process_live && wait_idx == 0) {
|
||||||
DWORD exit_code = 0;
|
DWORD exit_code = 0;
|
||||||
if(GetExitCodeProcess((HANDLE)process_handle.u64[0], &exit_code)) {
|
if(GetExitCodeProcess((HANDLE)process_handle.u64[0], &exit_code)) {
|
||||||
@@ -641,7 +616,7 @@ t_invoke_env(String8 exe_path, String8 cmdline, String8List env, U64 timeout_us)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
AssertAlways(pipe_idx != max_U64);
|
AssertAlways(pipe_idx != max_U64);
|
||||||
|
|
||||||
DWORD read_size;
|
DWORD read_size;
|
||||||
if (GetOverlappedResult(captures_win32[pipe_idx].read_pipe_handle, &captures_win32[pipe_idx].overlapped, &read_size, 0)) {
|
if (GetOverlappedResult(captures_win32[pipe_idx].read_pipe_handle, &captures_win32[pipe_idx].overlapped, &read_size, 0)) {
|
||||||
if (read_size > 0) {
|
if (read_size > 0) {
|
||||||
@@ -649,7 +624,7 @@ t_invoke_env(String8 exe_path, String8 cmdline, String8List env, U64 timeout_us)
|
|||||||
String8 string = str8(captures_win32[pipe_idx].buffer, read_size);
|
String8 string = str8(captures_win32[pipe_idx].buffer, read_size);
|
||||||
String8 string_copy = str8_copy(scratch.arena, string);
|
String8 string_copy = str8_copy(scratch.arena, string);
|
||||||
str8_list_push(scratch.arena, captures_win32[pipe_idx].parts, string_copy);
|
str8_list_push(scratch.arena, captures_win32[pipe_idx].parts, string_copy);
|
||||||
|
|
||||||
// queue next overlapped read
|
// queue next overlapped read
|
||||||
captures_win32[pipe_idx].state = Win32CaptureState_Null;
|
captures_win32[pipe_idx].state = Win32CaptureState_Null;
|
||||||
} else {
|
} else {
|
||||||
@@ -664,7 +639,7 @@ t_invoke_env(String8 exe_path, String8 cmdline, String8List env, U64 timeout_us)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// (timeout) kill process if alive so we can safeley cancel async IO
|
// (timeout) kill process if alive so we can safeley cancel async IO
|
||||||
if (is_process_live) {
|
if (is_process_live) {
|
||||||
if (TerminateProcess((HANDLE)process_handle.u64[0], 999)) {
|
if (TerminateProcess((HANDLE)process_handle.u64[0], 999)) {
|
||||||
@@ -672,20 +647,20 @@ t_invoke_env(String8 exe_path, String8 cmdline, String8List env, U64 timeout_us)
|
|||||||
Assert(0 && "process is taking too long to exit");
|
Assert(0 && "process is taking too long to exit");
|
||||||
}
|
}
|
||||||
} else { Assert(0 && "failed to kill process"); }
|
} else { Assert(0 && "failed to kill process"); }
|
||||||
|
|
||||||
DWORD exit_code = 0;
|
DWORD exit_code = 0;
|
||||||
if (GetExitCodeProcess((HANDLE)process_handle.u64[0], &exit_code)) {
|
if (GetExitCodeProcess((HANDLE)process_handle.u64[0], &exit_code)) {
|
||||||
g_last_exit_code = exit_code;
|
g_last_exit_code = exit_code;
|
||||||
} else { Assert(0 && "failed to get process exit code"); }
|
} else { Assert(0 && "failed to get process exit code"); }
|
||||||
}
|
}
|
||||||
|
|
||||||
// (timeout) cancel pending async IO
|
// (timeout) cancel pending async IO
|
||||||
for EachElement(i, captures_win32) {
|
for EachElement(i, captures_win32) {
|
||||||
if (captures_win32[i].state == Win32CaptureState_Pending) {
|
if (captures_win32[i].state == Win32CaptureState_Pending) {
|
||||||
BOOL cancel_ok = CancelIoEx(captures_win32[i].read_pipe_handle, &captures_win32[i].overlapped);
|
BOOL cancel_ok = CancelIoEx(captures_win32[i].read_pipe_handle, &captures_win32[i].overlapped);
|
||||||
DWORD cancel_error = cancel_ok ? ERROR_SUCCESS : GetLastError();
|
DWORD cancel_error = cancel_ok ? ERROR_SUCCESS : GetLastError();
|
||||||
AssertAlways(cancel_ok || cancel_error == ERROR_NOT_FOUND);
|
AssertAlways(cancel_ok || cancel_error == ERROR_NOT_FOUND);
|
||||||
|
|
||||||
DWORD read_size = 0;
|
DWORD read_size = 0;
|
||||||
if (GetOverlappedResult(captures_win32[i].read_pipe_handle, &captures_win32[i].overlapped, &read_size, 1)) {
|
if (GetOverlappedResult(captures_win32[i].read_pipe_handle, &captures_win32[i].overlapped, &read_size, 1)) {
|
||||||
if (read_size > 0) {
|
if (read_size > 0) {
|
||||||
@@ -700,7 +675,7 @@ t_invoke_env(String8 exe_path, String8 cmdline, String8List env, U64 timeout_us)
|
|||||||
captures_win32[i].state = Win32CaptureState_EOF;
|
captures_win32[i].state = Win32CaptureState_EOF;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// close windows specific handles
|
// close windows specific handles
|
||||||
CloseHandle((HANDLE)process_handle.u64[0]);
|
CloseHandle((HANDLE)process_handle.u64[0]);
|
||||||
for EachElement(i, captures_win32) {
|
for EachElement(i, captures_win32) {
|
||||||
@@ -713,14 +688,14 @@ t_invoke_env(String8 exe_path, String8 cmdline, String8List env, U64 timeout_us)
|
|||||||
close((int)write_capture_handles[i].u64[0]);
|
close((int)write_capture_handles[i].u64[0]);
|
||||||
MemoryZeroStruct(&write_capture_handles[i]);
|
MemoryZeroStruct(&write_capture_handles[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
pid_t pid = (pid_t)process_handle.u64[0];
|
pid_t pid = (pid_t)process_handle.u64[0];
|
||||||
int pidfd = syscall(SYS_pidfd_open, pid, 0);
|
int pidfd = syscall(SYS_pidfd_open, pid, 0);
|
||||||
if (pidfd < 0) {
|
if (pidfd < 0) {
|
||||||
fprintf(stderr, "ERROR: failed to translate pid(%d) to pidfd\n", pid);
|
fprintf(stderr, "ERROR: failed to translate pid(%d) to pidfd\n", pid);
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
B32 is_process_live = 1;
|
B32 is_process_live = 1;
|
||||||
U64 endt_us = ENDT_US(timeout_us);
|
U64 endt_us = ENDT_US(timeout_us);
|
||||||
U64 read_buffer_default_size = MB(1);
|
U64 read_buffer_default_size = MB(1);
|
||||||
@@ -729,12 +704,12 @@ t_invoke_env(String8 exe_path, String8 cmdline, String8List env, U64 timeout_us)
|
|||||||
for (;;) {
|
for (;;) {
|
||||||
struct pollfd fds[3] = {0};
|
struct pollfd fds[3] = {0};
|
||||||
int nfds = 0;
|
int nfds = 0;
|
||||||
|
|
||||||
// append process
|
// append process
|
||||||
if (is_process_live) {
|
if (is_process_live) {
|
||||||
fds[nfds++] = (struct pollfd){ .fd = pidfd, .events = POLLIN | POLLHUP };
|
fds[nfds++] = (struct pollfd){ .fd = pidfd, .events = POLLIN | POLLHUP };
|
||||||
}
|
}
|
||||||
|
|
||||||
// append pipes
|
// append pipes
|
||||||
for EachElement(i, captures_linux) {
|
for EachElement(i, captures_linux) {
|
||||||
if (captures_linux[i].is_live) {
|
if (captures_linux[i].is_live) {
|
||||||
@@ -742,17 +717,17 @@ t_invoke_env(String8 exe_path, String8 cmdline, String8List env, U64 timeout_us)
|
|||||||
fds[nfds++] = (struct pollfd){ .fd = captures_linux[i].fds[0], .events = POLLIN | POLLHUP };
|
fds[nfds++] = (struct pollfd){ .fd = captures_linux[i].fds[0], .events = POLLIN | POLLHUP };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// exit if there are no more handles to poll
|
// exit if there are no more handles to poll
|
||||||
if (nfds == 0) { break; }
|
if (nfds == 0) { break; }
|
||||||
|
|
||||||
// compute wait time
|
// compute wait time
|
||||||
int wait_ms = -1;
|
int wait_ms = -1;
|
||||||
if (timeout_us != max_U64) {
|
if (timeout_us != max_U64) {
|
||||||
U64 now_us = now_time_us();
|
U64 now_us = now_time_us();
|
||||||
wait_ms = now_us < endt_us ? ClampTop((endt_us - now_us + 999) / 1000, max_U32-1) : 0;
|
wait_ms = now_us < endt_us ? ClampTop((endt_us - now_us + 999) / 1000, max_U32-1) : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// wait for kernel to signal any of the wait handles
|
// wait for kernel to signal any of the wait handles
|
||||||
int poll_result = poll(fds, nfds, wait_ms);
|
int poll_result = poll(fds, nfds, wait_ms);
|
||||||
if (poll_result < 0) {
|
if (poll_result < 0) {
|
||||||
@@ -773,12 +748,12 @@ t_invoke_env(String8 exe_path, String8 cmdline, String8List env, U64 timeout_us)
|
|||||||
} else {
|
} else {
|
||||||
fprintf(stderr, "ERROR: failed to reap process %d\n", pid);
|
fprintf(stderr, "ERROR: failed to reap process %d\n", pid);
|
||||||
}
|
}
|
||||||
|
|
||||||
// signal on process fd means exit
|
// signal on process fd means exit
|
||||||
is_process_live = 0;
|
is_process_live = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for EachElement(i, captures_linux) {
|
for EachElement(i, captures_linux) {
|
||||||
if (captures_linux[i].is_live) {
|
if (captures_linux[i].is_live) {
|
||||||
if (captures_linux[i].poll_fd->revents & POLLIN) {
|
if (captures_linux[i].poll_fd->revents & POLLIN) {
|
||||||
@@ -787,7 +762,7 @@ t_invoke_env(String8 exe_path, String8 cmdline, String8List env, U64 timeout_us)
|
|||||||
read_buffer_size = read_buffer_default_size;
|
read_buffer_size = read_buffer_default_size;
|
||||||
read_buffer = push_array(scratch.arena, U8, read_buffer_default_size);
|
read_buffer = push_array(scratch.arena, U8, read_buffer_default_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
ssize_t read_size = LNX_RETRY_ON_EINTR(read(captures_linux[i].poll_fd->fd, read_buffer, read_buffer_size));
|
ssize_t read_size = LNX_RETRY_ON_EINTR(read(captures_linux[i].poll_fd->fd, read_buffer, read_buffer_size));
|
||||||
if (read_size > 0) {
|
if (read_size > 0) {
|
||||||
str8_list_push(scratch.arena, captures_linux[i].parts, str8(read_buffer, read_size));
|
str8_list_push(scratch.arena, captures_linux[i].parts, str8(read_buffer, read_size));
|
||||||
@@ -807,40 +782,40 @@ t_invoke_env(String8 exe_path, String8 cmdline, String8List env, U64 timeout_us)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (captures_linux[i].poll_fd->revents & POLLHUP) {
|
if (captures_linux[i].poll_fd->revents & POLLHUP) {
|
||||||
captures_linux[i].is_live = 0;
|
captures_linux[i].is_live = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// close process handle
|
// close process handle
|
||||||
if (close(pidfd) < 0) {
|
if (close(pidfd) < 0) {
|
||||||
fprintf(stderr, "ERROR: failed to close process handle %d\n", pidfd);
|
fprintf(stderr, "ERROR: failed to close process handle %d\n", pidfd);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
t_infof("Invoke: {\n");
|
t_infof("Invoke: {\n");
|
||||||
t_infof(" CMDL: %S\n", full_cmd_line);
|
t_infof(" CMDL: %S\n", full_cmd_line);
|
||||||
t_infof(" WDIR: %S\n", g_wdir);
|
t_infof(" WDIR: %S\n", g_wdir);
|
||||||
t_infof(" Exit: %u\n", g_last_exit_code);
|
t_infof(" Exit: %u\n", g_last_exit_code);
|
||||||
t_infof("}\n");
|
t_infof("}\n");
|
||||||
|
|
||||||
// update output global
|
// update output global
|
||||||
g_output = str8_list_join(g_output_arena, &stdout_parts, 0);
|
g_output = str8_list_join(g_output_arena, &stdout_parts, 0);
|
||||||
g_errors = str8_list_join(g_output_arena, &stderr_parts, 0);
|
g_errors = str8_list_join(g_output_arena, &stderr_parts, 0);
|
||||||
|
|
||||||
// write to the output file
|
// write to the output file
|
||||||
if (g_redirect_stdout) {
|
if (g_redirect_stdout) {
|
||||||
write_data_to_file_path(g_stdout_file_name, g_output);
|
write_data_to_file_path(g_stdout_file_name, g_output);
|
||||||
}
|
}
|
||||||
|
|
||||||
is_ok = 1; // process was launched (does not mean exited successfully)
|
is_ok = 1; // process was launched (does not mean exited successfully)
|
||||||
exit:;
|
exit:;
|
||||||
for EachElement(i, read_capture_handles) { file_close(read_capture_handles[i]); }
|
for EachElement(i, read_capture_handles) { file_close(read_capture_handles[i]); }
|
||||||
for EachElement(i, write_capture_handles) { file_close(write_capture_handles[i]); }
|
for EachElement(i, write_capture_handles) { file_close(write_capture_handles[i]); }
|
||||||
|
|
||||||
scratch_end(scratch);
|
scratch_end(scratch);
|
||||||
return is_ok;
|
return is_ok;
|
||||||
}
|
}
|
||||||
@@ -1104,6 +1079,8 @@ t_help(void)
|
|||||||
fprintf(stderr, " torture +* Force-run all tests\n");
|
fprintf(stderr, " torture +* Force-run all tests\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal void t_dbg_register_script_tests(Arena *arena, String8 folder_path);
|
||||||
|
|
||||||
internal void
|
internal void
|
||||||
t_entry_point(CmdLine *cmdline)
|
t_entry_point(CmdLine *cmdline)
|
||||||
{
|
{
|
||||||
@@ -1127,25 +1104,25 @@ t_entry_point(CmdLine *cmdline)
|
|||||||
//
|
//
|
||||||
{
|
{
|
||||||
B32 print_help = cmd_line_has_flag(cmdline, str8_lit("help")) ||
|
B32 print_help = cmd_line_has_flag(cmdline, str8_lit("help")) ||
|
||||||
cmd_line_has_flag(cmdline, str8_lit("h"));
|
cmd_line_has_flag(cmdline, str8_lit("h"));
|
||||||
if (print_help) {
|
if (print_help) {
|
||||||
t_help();
|
t_help();
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Gather tests
|
// Gather tests
|
||||||
{
|
{
|
||||||
// register debugger tests
|
// register debugger tests
|
||||||
String8 test_folder_path = str8f(scratch.arena, "%S/torture/dbg_tests", t_src_path());
|
String8 test_folder_path = str8f(scratch.arena, "%S/torture/dbg_tests", t_src_path());
|
||||||
t_dbg_register_script_tests(scratch.arena, test_folder_path);
|
t_dbg_register_script_tests(scratch.arena, test_folder_path);
|
||||||
|
|
||||||
// sort tests
|
// sort tests
|
||||||
g_torture_tests = push_array(scratch.arena, T_Test *, g_torture_test_count);
|
g_torture_tests = push_array(scratch.arena, T_Test *, g_torture_test_count);
|
||||||
for EachIndex(i, g_torture_test_count) { g_torture_tests[i] = &g_torture_tests_[i]; }
|
for EachIndex(i, g_torture_test_count) { g_torture_tests[i] = &g_torture_tests_[i]; }
|
||||||
radsort(g_torture_tests, g_torture_test_count, t_test_ptr_is_before);
|
radsort(g_torture_tests, g_torture_test_count, t_test_ptr_is_before);
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Handle -list
|
// Handle -list
|
||||||
//
|
//
|
||||||
@@ -1167,7 +1144,7 @@ t_entry_point(CmdLine *cmdline)
|
|||||||
g_clang_path = cmd_line_string(cmdline, str8_lit("clang"));
|
g_clang_path = cmd_line_string(cmdline, str8_lit("clang"));
|
||||||
g_gcc_path = cmd_line_string(cmdline, str8_lit("gcc"));
|
g_gcc_path = cmd_line_string(cmdline, str8_lit("gcc"));
|
||||||
g_linker_path = cmd_line_string(cmdline, str8_lit("linker"));
|
g_linker_path = cmd_line_string(cmdline, str8_lit("linker"));
|
||||||
|
|
||||||
//
|
//
|
||||||
// Handle -test_data
|
// Handle -test_data
|
||||||
//
|
//
|
||||||
@@ -1185,63 +1162,63 @@ t_entry_point(CmdLine *cmdline)
|
|||||||
U64List targets = {0};
|
U64List targets = {0};
|
||||||
{
|
{
|
||||||
String8List inputs = {0};
|
String8List inputs = {0};
|
||||||
|
|
||||||
CmdLineOpt *target_opt = 0;
|
CmdLineOpt *target_opt = 0;
|
||||||
if (target_opt == 0) { target_opt = cmd_line_opt_from_string(cmdline, str8_lit("target")); }
|
if (target_opt == 0) { target_opt = cmd_line_opt_from_string(cmdline, str8_lit("target")); }
|
||||||
if (target_opt == 0) { target_opt = cmd_line_opt_from_string(cmdline, str8_lit("t")); }
|
if (target_opt == 0) { target_opt = cmd_line_opt_from_string(cmdline, str8_lit("t")); }
|
||||||
|
|
||||||
// handle explicit target switch
|
// handle explicit target switch
|
||||||
if (target_opt) {
|
if (target_opt) {
|
||||||
str8_list_concat_in_place(&inputs, &target_opt->value_strings);
|
str8_list_concat_in_place(&inputs, &target_opt->value_strings);
|
||||||
}
|
}
|
||||||
|
|
||||||
// accept inputs from the command line as target tests to run
|
// accept inputs from the command line as target tests to run
|
||||||
str8_list_concat_in_place(&inputs, &cmdline->inputs);
|
str8_list_concat_in_place(&inputs, &cmdline->inputs);
|
||||||
|
|
||||||
// no inputs -> print help and exit
|
// no inputs -> print help and exit
|
||||||
if (inputs.node_count == 0) {
|
if (inputs.node_count == 0) {
|
||||||
t_help();
|
t_help();
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
HashMap hm = {0};
|
HashMap hm = {0};
|
||||||
for EachNode(input_n, String8Node, inputs.first) {
|
for EachNode(input_n, String8Node, inputs.first) {
|
||||||
String8 t = input_n->string;
|
String8 t = input_n->string;
|
||||||
|
|
||||||
// parse mode
|
// parse mode
|
||||||
typedef enum { Mode_Default, Mode_Skip, Mode_Force, } Mode;
|
typedef enum { Mode_Default, Mode_Skip, Mode_Force, } Mode;
|
||||||
Mode mode = Mode_Default;
|
Mode mode = Mode_Default;
|
||||||
if (str8_match_wildcard(t, str8_lit("+*"), 0)) { mode = Mode_Force; t = str8_skip(t, 1); }
|
if (str8_match_wildcard(t, str8_lit("+*"), 0)) { mode = Mode_Force; t = str8_skip(t, 1); }
|
||||||
else if (str8_match_wildcard(t, str8_lit("!*"), 0)) { mode = Mode_Skip; t = str8_skip(t, 1); }
|
else if (str8_match_wildcard(t, str8_lit("!*"), 0)) { mode = Mode_Skip; t = str8_skip(t, 1); }
|
||||||
|
|
||||||
if (str8_find_needle(t, 0, str8_lit("/"), 0) >= t.size) {
|
if (str8_find_needle(t, 0, str8_lit("/"), 0) >= t.size) {
|
||||||
t = str8f(scratch.arena, "*/%S", t);
|
t = str8f(scratch.arena, "*/%S", t);
|
||||||
}
|
}
|
||||||
|
|
||||||
U64 match_count = 0;
|
U64 match_count = 0;
|
||||||
|
|
||||||
for EachIndex(test_idx, g_torture_test_count) {
|
for EachIndex(test_idx, g_torture_test_count) {
|
||||||
// match test names
|
// match test names
|
||||||
String8 test_name = t_test_name_from_idx(scratch.arena, test_idx);
|
String8 test_name = t_test_name_from_idx(scratch.arena, test_idx);
|
||||||
|
|
||||||
if (str8_match_wildcard(test_name, t, StringMatchFlag_CaseInsensitive)) {
|
if (str8_match_wildcard(test_name, t, StringMatchFlag_CaseInsensitive)) {
|
||||||
// set skip flag
|
// set skip flag
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
case Mode_Default: break;
|
case Mode_Default: break;
|
||||||
case Mode_Skip: g_torture_tests[test_idx]->skip = 1; break;
|
case Mode_Skip: g_torture_tests[test_idx]->skip = 1; break;
|
||||||
case Mode_Force: g_torture_tests[test_idx]->skip = 0; break;
|
case Mode_Force: g_torture_tests[test_idx]->skip = 0; break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// append test when not in skipping mode
|
// append test when not in skipping mode
|
||||||
if ( ! hash_map_search_string_u64(&hm, test_name)) {
|
if ( ! hash_map_search_string_u64(&hm, test_name)) {
|
||||||
hash_map_push_string_u64(scratch.arena, &hm, test_name, 1);
|
hash_map_push_string_u64(scratch.arena, &hm, test_name, 1);
|
||||||
u64_list_push(scratch.arena, &targets, test_idx);
|
u64_list_push(scratch.arena, &targets, test_idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
match_count += 1;
|
match_count += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (match_count == 0) {
|
if (match_count == 0) {
|
||||||
fprintf(stderr, "WARNING: no matches found for input: %.*s\n", str8_varg(input_n->string));
|
fprintf(stderr, "WARNING: no matches found for input: %.*s\n", str8_varg(input_n->string));
|
||||||
}
|
}
|
||||||
@@ -1253,13 +1230,13 @@ t_entry_point(CmdLine *cmdline)
|
|||||||
g_stop_on_first_fail_or_crash = !cmd_line_has_flag(cmdline, str8_lit("keep_going"));
|
g_stop_on_first_fail_or_crash = !cmd_line_has_flag(cmdline, str8_lit("keep_going"));
|
||||||
g_build_only = cmd_line_has_flag(cmdline, str8_lit("build_only"));
|
g_build_only = cmd_line_has_flag(cmdline, str8_lit("build_only"));
|
||||||
g_output_arena = arena_alloc();
|
g_output_arena = arena_alloc();
|
||||||
|
|
||||||
// default options when running under debugger
|
// default options when running under debugger
|
||||||
#if OS_WINDOWS
|
#if OS_WINDOWS
|
||||||
if (!cmd_line_has_flag(cmdline, str8_lit("print_stdout")) && IsDebuggerPresent()) {
|
if (!cmd_line_has_flag(cmdline, str8_lit("print_stdout")) && IsDebuggerPresent()) {
|
||||||
g_redirect_stdout = 0;
|
g_redirect_stdout = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// automatically close child processes on exit
|
// automatically close child processes on exit
|
||||||
{
|
{
|
||||||
HANDLE job_handle = CreateJobObjectA(0, 0);
|
HANDLE job_handle = CreateJobObjectA(0, 0);
|
||||||
@@ -1310,19 +1287,19 @@ t_entry_point(CmdLine *cmdline)
|
|||||||
max_group_size = Max(max_group_size, t_group_from_test_idx(test_idx).size);
|
max_group_size = Max(max_group_size, t_group_from_test_idx(test_idx).size);
|
||||||
}
|
}
|
||||||
|
|
||||||
U64 run_counters[T_RunStatus_Count] = {0};
|
U64 run_counters[TestStatus_COUNT] = {0};
|
||||||
U64 max_digit_count = count_digits_u64(target_indices.count, 10);
|
U64 max_digit_count = count_digits_u64(target_indices.count, 10);
|
||||||
U64 total_time_start = now_time_us();
|
U64 total_time_start = now_time_us();
|
||||||
|
|
||||||
typedef struct { U64 target_idx, d; } Slowest;
|
typedef struct { U64 target_idx, d; } Slowest;
|
||||||
Slowest slowest[5] = {0};
|
Slowest slowest[5] = {0};
|
||||||
for EachElement(i, slowest) { slowest[i].target_idx = max_U64; }
|
for EachElement(i, slowest) { slowest[i].target_idx = max_U64; }
|
||||||
|
|
||||||
U64List skipped_tests = {0};
|
U64List skipped_tests = {0};
|
||||||
|
|
||||||
for EachIndex(i, target_indices.count) {
|
for EachIndex(i, target_indices.count) {
|
||||||
if (i == 0) { PrintHeader("Tests"); }
|
if (i == 0) { PrintHeader("Tests"); }
|
||||||
|
|
||||||
U64 target_idx = target_indices.v[i];
|
U64 target_idx = target_indices.v[i];
|
||||||
T_Test *test = g_torture_tests[target_idx];
|
T_Test *test = g_torture_tests[target_idx];
|
||||||
|
|
||||||
@@ -1335,11 +1312,11 @@ t_entry_point(CmdLine *cmdline)
|
|||||||
fprintf(stdout, "%.*s %.*s/ %s", str8_varg(t_group_from_test(test)), (int)(max_group_size - t_group_from_test(test).size), spaces, test->label);
|
fprintf(stdout, "%.*s %.*s/ %s", str8_varg(t_group_from_test(test)), (int)(max_group_size - t_group_from_test(test).size), spaces, test->label);
|
||||||
fprintf(stdout, " %.*s ", (int)dots_count, dots);
|
fprintf(stdout, " %.*s ", (int)dots_count, dots);
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
|
|
||||||
// setup output directory
|
// setup output directory
|
||||||
g_wdir = push_str8f(scratch.arena, "%S/%s", g_out, test->label);
|
g_wdir = push_str8f(scratch.arena, "%S/%s", g_out, test->label);
|
||||||
g_wdir = full_path_from_path(scratch.arena, g_wdir);
|
g_wdir = full_path_from_path(scratch.arena, g_wdir);
|
||||||
|
|
||||||
// delete files from last run in the work directory
|
// delete files from last run in the work directory
|
||||||
if (folder_path_exists(g_wdir)) {
|
if (folder_path_exists(g_wdir)) {
|
||||||
t_delete_dir(g_wdir);
|
t_delete_dir(g_wdir);
|
||||||
@@ -1353,21 +1330,32 @@ t_entry_point(CmdLine *cmdline)
|
|||||||
|
|
||||||
// run test
|
// run test
|
||||||
U64 run_start_time = now_time_us();
|
U64 run_start_time = now_time_us();
|
||||||
T_RunResult result = t_run(test, test->user_data);
|
TestResult result = t_run(test, test->user_data);
|
||||||
U64 run_end_time = now_time_us();
|
U64 run_end_time = now_time_us();
|
||||||
|
|
||||||
// update
|
// update
|
||||||
run_counters[result.status] += 1;
|
run_counters[result.status] += 1;
|
||||||
|
|
||||||
// print run status
|
|
||||||
fprintf(stdout, "%s%s" T_RESET, t_color_from_result(result.status), t_string_from_result(result.status));
|
|
||||||
|
|
||||||
if (result.status == T_RunStatus_Pass) {
|
// rjf: map status -> color / string
|
||||||
|
char *status_name_cstr = 0;
|
||||||
|
char *color_cstr = 0;
|
||||||
|
switch(result.status)
|
||||||
|
{
|
||||||
|
default:
|
||||||
|
case TestStatus_Pass: {status_name_cstr = "PASS"; color_cstr = T_GREEN;}break;
|
||||||
|
case TestStatus_Fail: {status_name_cstr = "FAIL"; color_cstr = T_RED;}break;
|
||||||
|
case TestStatus_Crash:{status_name_cstr = "CRASH"; color_cstr = T_RED;}break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// print run status
|
||||||
|
fprintf(stdout, "%s%s" T_RESET, color_cstr, status_name_cstr);
|
||||||
|
|
||||||
|
if (result.status == TestStatus_Pass) {
|
||||||
U64 d = run_end_time - run_start_time;
|
U64 d = run_end_time - run_start_time;
|
||||||
DateTime t = date_time_from_micro_seconds(d);
|
DateTime t = date_time_from_micro_seconds(d);
|
||||||
String8 s = string_from_elapsed_time(scratch.arena, t);
|
String8 s = string_from_elapsed_time(scratch.arena, t);
|
||||||
fprintf(stdout, " %.*s", str8_varg(s));
|
fprintf(stdout, " %.*s", str8_varg(s));
|
||||||
|
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
|
|
||||||
U64 insert_idx = max_U64;
|
U64 insert_idx = max_U64;
|
||||||
@@ -1387,15 +1375,15 @@ t_entry_point(CmdLine *cmdline)
|
|||||||
}
|
}
|
||||||
fprintf(stdout, "\n");
|
fprintf(stdout, "\n");
|
||||||
|
|
||||||
if (result.status == T_RunStatus_Fail) {
|
if (result.status == TestStatus_Fail) {
|
||||||
fprintf(stdout, " ERROR: %s:%d: condition: \"%s\"\n", result.fail_file, result.fail_line, result.fail_cond);
|
fprintf(stdout, " ERROR: %s:%d: condition: \"%s\"\n", result.fail_file, result.fail_line, result.fail_cond);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result.status == T_RunStatus_Fail || result.status == T_RunStatus_Crash) {
|
if (result.status == TestStatus_Fail || result.status == TestStatus_Crash) {
|
||||||
if (g_stop_on_first_fail_or_crash) { goto exit; }
|
if (g_stop_on_first_fail_or_crash) { goto exit; }
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result.status == T_RunStatus_Skip) {
|
if (result.status == TestStatus_Skip) {
|
||||||
u64_list_push(scratch.arena, &skipped_tests, target_idx);
|
u64_list_push(scratch.arena, &skipped_tests, target_idx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1404,22 +1392,22 @@ t_entry_point(CmdLine *cmdline)
|
|||||||
if (target_indices.count > 0 && sum_array_u64(ArrayCount(run_counters), run_counters) > 0) {
|
if (target_indices.count > 0 && sum_array_u64(ArrayCount(run_counters), run_counters) > 0) {
|
||||||
U64 total_time_dt = total_time_end - total_time_start;
|
U64 total_time_dt = total_time_end - total_time_start;
|
||||||
String8 total_time_str = string_from_elapsed_time(scratch.arena, date_time_from_micro_seconds(total_time_dt));
|
String8 total_time_str = string_from_elapsed_time(scratch.arena, date_time_from_micro_seconds(total_time_dt));
|
||||||
|
|
||||||
fprintf(stderr, "\n");
|
fprintf(stderr, "\n");
|
||||||
PrintHeader("Summary");
|
PrintHeader("Summary");
|
||||||
fprintf(stderr, " Passed %llu\n", (unsigned long long)run_counters[T_RunStatus_Pass]);
|
fprintf(stderr, " Passed %llu\n", (unsigned long long)run_counters[TestStatus_Pass]);
|
||||||
fprintf(stderr, " Failed %llu\n", (unsigned long long)run_counters[T_RunStatus_Fail]);
|
fprintf(stderr, " Failed %llu\n", (unsigned long long)run_counters[TestStatus_Fail]);
|
||||||
fprintf(stderr, " Crashed %llu\n", (unsigned long long)run_counters[T_RunStatus_Crash]);
|
fprintf(stderr, " Crashed %llu\n", (unsigned long long)run_counters[TestStatus_Crash]);
|
||||||
fprintf(stderr, " Skipped %llu\n", (unsigned long long)run_counters[T_RunStatus_Skip]);
|
fprintf(stderr, " Skipped %llu\n", (unsigned long long)run_counters[TestStatus_Skip]);
|
||||||
fprintf(stderr, " Time %.*s\n", str8_varg(total_time_str));
|
fprintf(stderr, " Time %.*s\n", str8_varg(total_time_str));
|
||||||
|
|
||||||
U64 slow_count = 0;
|
U64 slow_count = 0;
|
||||||
for EachElement(i, slowest) {
|
for EachElement(i, slowest) {
|
||||||
Slowest s = slowest[i];
|
Slowest s = slowest[i];
|
||||||
if (s.target_idx >= g_torture_test_count) { break; }
|
if (s.target_idx >= g_torture_test_count) { break; }
|
||||||
slow_count += 1;
|
slow_count += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (slow_count > 3) {
|
if (slow_count > 3) {
|
||||||
U64 label_max = 0;
|
U64 label_max = 0;
|
||||||
U64 group_max = 0;
|
U64 group_max = 0;
|
||||||
@@ -1428,7 +1416,7 @@ t_entry_point(CmdLine *cmdline)
|
|||||||
label_max = Max(strlen(g_torture_tests[s.target_idx]->label), label_max);
|
label_max = Max(strlen(g_torture_tests[s.target_idx]->label), label_max);
|
||||||
group_max = Max(t_group_from_test_idx(s.target_idx).size, group_max);
|
group_max = Max(t_group_from_test_idx(s.target_idx).size, group_max);
|
||||||
}
|
}
|
||||||
|
|
||||||
fprintf(stderr, " \nSlow Tests\n");
|
fprintf(stderr, " \nSlow Tests\n");
|
||||||
for EachElement(i, slowest) {
|
for EachElement(i, slowest) {
|
||||||
Slowest s = slowest[i];
|
Slowest s = slowest[i];
|
||||||
@@ -1444,10 +1432,10 @@ t_entry_point(CmdLine *cmdline)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
exit_code = run_counters[T_RunStatus_Fail] + run_counters[T_RunStatus_Crash];
|
exit_code = run_counters[TestStatus_Fail] + run_counters[TestStatus_Crash];
|
||||||
exit:;
|
exit:;
|
||||||
}
|
}
|
||||||
|
|
||||||
scratch_end(scratch);
|
scratch_end(scratch);
|
||||||
exit(exit_code);
|
exit(exit_code);
|
||||||
}
|
}
|
||||||
|
|||||||
+20
-63
@@ -11,31 +11,8 @@
|
|||||||
#define T_YELLOW "\x1b[33m"
|
#define T_YELLOW "\x1b[33m"
|
||||||
#define T_BLUE "\x1b[34m"
|
#define T_BLUE "\x1b[34m"
|
||||||
|
|
||||||
//#define X(n, c)
|
#define T_RunSig(name) void t_##name(Arena *arena, String8 user_data, TestResult *result_out, String8List *test_out)
|
||||||
#define T_Run_XList \
|
typedef void (*T_Run)(Arena *arena, String8 user_data, TestResult *result_out, String8List *test_out);
|
||||||
X(Fail, T_RED) \
|
|
||||||
X(Crash, T_RED) \
|
|
||||||
X(Pass, T_GREEN) \
|
|
||||||
X(Skip, T_RESET)
|
|
||||||
|
|
||||||
typedef enum
|
|
||||||
{
|
|
||||||
#define X(n,...) T_RunStatus_##n,
|
|
||||||
T_Run_XList
|
|
||||||
#undef X
|
|
||||||
T_RunStatus_Count
|
|
||||||
} T_RunStatus;
|
|
||||||
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
T_RunStatus status;
|
|
||||||
char *fail_file;
|
|
||||||
int fail_line;
|
|
||||||
char *fail_cond;
|
|
||||||
} T_RunResult;
|
|
||||||
|
|
||||||
#define T_RunSig(name) void t_##name(Arena *arena, String8 user_data, T_RunResult *result_out, String8List *test_out)
|
|
||||||
typedef void (*T_Run)(Arena *arena, String8 user_data, T_RunResult *result_out, String8List *test_out);
|
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
@@ -50,26 +27,10 @@ typedef struct
|
|||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
T_Test *test;
|
T_Test *test;
|
||||||
String8 user_data;
|
String8 user_data;
|
||||||
T_RunResult result;
|
TestResult result;
|
||||||
} T_RunCtx;
|
} T_RunCtx;
|
||||||
|
|
||||||
typedef enum
|
|
||||||
{
|
|
||||||
T_Compiler_Null,
|
|
||||||
T_Compiler_Cl,
|
|
||||||
T_Compiler_Clang,
|
|
||||||
T_Compiler_Gcc,
|
|
||||||
} T_Compiler;
|
|
||||||
|
|
||||||
typedef enum
|
|
||||||
{
|
|
||||||
T_Linker_Null,
|
|
||||||
T_Linker_RAD,
|
|
||||||
T_Linker_MSVC,
|
|
||||||
T_Linker_LLVM
|
|
||||||
} T_Linker;
|
|
||||||
|
|
||||||
extern U64 g_torture_test_count;
|
extern U64 g_torture_test_count;
|
||||||
extern T_Test **g_torture_tests;
|
extern T_Test **g_torture_tests;
|
||||||
extern T_Test g_torture_tests_[0xffffff];
|
extern T_Test g_torture_tests_[0xffffff];
|
||||||
@@ -77,34 +38,30 @@ extern T_Test g_torture_tests_[0xffffff];
|
|||||||
internal void t_break_if_debugger_present(void);
|
internal void t_break_if_debugger_present(void);
|
||||||
|
|
||||||
#define T_AddTest(name, f, l, skip, ...) \
|
#define T_AddTest(name, f, l, skip, ...) \
|
||||||
T_RunSig(name); \
|
T_RunSig(name); \
|
||||||
__VA_ARGS__ void t_add_test_##name(void) \
|
__VA_ARGS__ void t_add_test_##name(void) \
|
||||||
{ \
|
{ \
|
||||||
g_torture_tests_[g_torture_test_count++] = (T_Test){ f, Stringify(name), l, &t_##name, skip }; \
|
g_torture_tests_[g_torture_test_count++] = (T_Test){ f, Stringify(name), l, &t_##name, skip }; \
|
||||||
}
|
}
|
||||||
|
|
||||||
#if COMPILER_MSVC
|
#if COMPILER_MSVC
|
||||||
# pragma section(".CRT$XCU", read)
|
# pragma section(".CRT$XCU", read)
|
||||||
# define TEST_(name, skip) \
|
# define TEST_(name, skip) \
|
||||||
T_AddTest(name, __FILE__, __LINE__, skip) \
|
T_AddTest(name, __FILE__, __LINE__, skip) \
|
||||||
__declspec(allocate(".CRT$XCU")) void(*r_##name)(void) = t_add_test_##name; \
|
__declspec(allocate(".CRT$XCU")) void(*r_##name)(void) = t_add_test_##name; \
|
||||||
__pragma(comment(linker, "/include:" Stringify(r_##name)))
|
__pragma(comment(linker, "/include:" Stringify(r_##name)))
|
||||||
#else
|
#else
|
||||||
# define TEST_(name, skip) T_AddTest(name, __FILE__, __LINE__, skip, __attribute__((constructor)))
|
# define TEST_(name, skip) T_AddTest(name, __FILE__, __LINE__, skip, __attribute__((constructor)))
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define TEST(name) \
|
#define TEST(name) \
|
||||||
TEST_(name, 0) \
|
TEST_(name, 0) \
|
||||||
T_RunSig(name)
|
T_RunSig(name)
|
||||||
#define SKIP(name) \
|
#define SKIP(name) \
|
||||||
TEST_(name, 1) \
|
TEST_(name, 1) \
|
||||||
T_RunSig(name)
|
T_RunSig(name)
|
||||||
|
|
||||||
#define T_Ok(c) do { if (!(c)) { \
|
#define T_Ok(c) TestCheck(c)
|
||||||
*result_out = (T_RunResult){ .fail_file = __FILE__, .fail_line = __LINE__, .fail_cond = Stringify(c) }; \
|
|
||||||
t_break_if_debugger_present(); \
|
|
||||||
return; \
|
|
||||||
} } while(0)
|
|
||||||
|
|
||||||
#define T_MatchLinef(out, ...) T_Ok(t_match_linef(out, __VA_ARGS__))
|
#define T_MatchLinef(out, ...) T_Ok(t_match_linef(out, __VA_ARGS__))
|
||||||
#define t_outf(...) str8_list_pushf(arena, test_out, ## __VA_ARGS__)
|
#define t_outf(...) str8_list_pushf(arena, test_out, ## __VA_ARGS__)
|
||||||
@@ -129,9 +86,9 @@ internal B32 t_delete_file(String8 name);
|
|||||||
internal String8 t_make_file_path(Arena *arena, String8 name);
|
internal String8 t_make_file_path(Arena *arena, String8 name);
|
||||||
|
|
||||||
// test runner
|
// test runner
|
||||||
internal void t_run_caller(void *raw_ctx);
|
internal void t_run_caller(void *raw_ctx);
|
||||||
internal void t_run_fail_handler(void *raw_ctx);
|
internal void t_run_fail_handler(void *raw_ctx);
|
||||||
internal T_RunResult t_run(T_Test *test, String8 user_data);
|
internal TestResult t_run(T_Test *test, String8 user_data);
|
||||||
|
|
||||||
// tools
|
// tools
|
||||||
internal String8 t_radbin_path(void);
|
internal String8 t_radbin_path(void);
|
||||||
|
|||||||
@@ -1,205 +0,0 @@
|
|||||||
// Copyright (c) Epic Games Tools
|
|
||||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
////////////////////////////////
|
|
||||||
// IPC Controller
|
|
||||||
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
B32 running;
|
|
||||||
U64 run_gen;
|
|
||||||
U64 ip;
|
|
||||||
Arch arch;
|
|
||||||
U64 vaddr_min;
|
|
||||||
U64 vaddr_max;
|
|
||||||
U64 ip_vaddr;
|
|
||||||
U64 sp_base;
|
|
||||||
U64 tls_root;
|
|
||||||
U64 tls_index;
|
|
||||||
U64 tls_offset;
|
|
||||||
U64 timestamp;
|
|
||||||
U64 exception_code;
|
|
||||||
U64 bp_flags;
|
|
||||||
String8 string;
|
|
||||||
OperatingSystem target_os;
|
|
||||||
U64 tls_model;
|
|
||||||
String8 stop_cause;
|
|
||||||
} T_DbgState;
|
|
||||||
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
String8 file_path;
|
|
||||||
TxtPt pt;
|
|
||||||
Rng1U64 voff_range;
|
|
||||||
} T_DbgLine;
|
|
||||||
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
U64 count;
|
|
||||||
T_DbgLine *v;
|
|
||||||
} T_DbgLineArray;
|
|
||||||
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
String8 expr;
|
|
||||||
String8 value;
|
|
||||||
String8 type;
|
|
||||||
String8 error;
|
|
||||||
} T_Eval;
|
|
||||||
|
|
||||||
////////////////////////////////
|
|
||||||
// Dbg Script
|
|
||||||
|
|
||||||
#define T_DbgScriptCmdKind_XList \
|
|
||||||
X(Breakpoint, "bp") \
|
|
||||||
X(ClearBreakpoints, "bp_clear") \
|
|
||||||
X(Halt, "halt") \
|
|
||||||
X(Run, "run") \
|
|
||||||
X(RunToLine, "run_to_line") \
|
|
||||||
X(StepOver, "step_over") \
|
|
||||||
X(StepInto, "step_into") \
|
|
||||||
X(StepOut, "step_out") \
|
|
||||||
X(StepOverInst, "step_over_inst") \
|
|
||||||
X(StepIntoInst, "step_over_inst") \
|
|
||||||
X(StepOverLine, "step_over_line") \
|
|
||||||
X(StepIntoLine, "step_into_line") \
|
|
||||||
X(KillAll, "kll_all") \
|
|
||||||
X(At, "at") \
|
|
||||||
X(Eval, "eval")
|
|
||||||
|
|
||||||
typedef enum
|
|
||||||
{
|
|
||||||
T_DbgScriptCmdKind_Null,
|
|
||||||
#define X(n,...) T_DbgScriptCmdKind_##n,
|
|
||||||
T_DbgScriptCmdKind_XList
|
|
||||||
#undef X
|
|
||||||
T_DbgScriptCmdKind_Count
|
|
||||||
} T_DbgScriptCmdKind;
|
|
||||||
|
|
||||||
#define T_DbgScriptDirectiveKind_XList \
|
|
||||||
X(Compile, "compile") \
|
|
||||||
X(Link, "link") \
|
|
||||||
X(Launch, "launch") \
|
|
||||||
X(Skip, "skip")
|
|
||||||
|
|
||||||
typedef enum
|
|
||||||
{
|
|
||||||
T_DbgScriptDirectiveKind_Null,
|
|
||||||
#define X(q,w) T_DbgScriptDirectiveKind_##q,
|
|
||||||
T_DbgScriptDirectiveKind_XList
|
|
||||||
#undef X
|
|
||||||
T_DbgScriptDirectiveKind_Count,
|
|
||||||
} T_DbgScriptDirectiveKind;
|
|
||||||
|
|
||||||
typedef struct T_DbgScriptFile
|
|
||||||
{
|
|
||||||
struct T_DbgScriptFile *next;
|
|
||||||
String8 path;
|
|
||||||
String8 source;
|
|
||||||
TxtPt pt;
|
|
||||||
} T_DbgScriptFile;
|
|
||||||
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
U64 count;
|
|
||||||
T_DbgScriptFile *first;
|
|
||||||
T_DbgScriptFile *last;
|
|
||||||
} T_DbgScriptFileList;
|
|
||||||
|
|
||||||
typedef struct T_DbgScriptCmd
|
|
||||||
{
|
|
||||||
struct T_DbgScriptCmd *next;
|
|
||||||
T_DbgScriptCmdKind kind;
|
|
||||||
TxtPt pt;
|
|
||||||
S64 at_delta;
|
|
||||||
} T_DbgScriptCmd;
|
|
||||||
|
|
||||||
typedef struct T_DbgScriptProgram
|
|
||||||
{
|
|
||||||
TxtPt pt;
|
|
||||||
U64 order;
|
|
||||||
OperatingSystem os;
|
|
||||||
T_DbgScriptFile *file;
|
|
||||||
U64 count;
|
|
||||||
T_DbgScriptCmd *first;
|
|
||||||
T_DbgScriptCmd *last;
|
|
||||||
struct T_DbgScriptProgram *next;
|
|
||||||
} T_DbgScriptProgram;
|
|
||||||
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
U64 count;
|
|
||||||
T_DbgScriptProgram *first;
|
|
||||||
T_DbgScriptProgram *last;
|
|
||||||
} T_DbgScriptProgramList;
|
|
||||||
|
|
||||||
typedef struct T_DbgScriptDirective
|
|
||||||
{
|
|
||||||
struct T_DbgScriptDirective *next;
|
|
||||||
T_DbgScriptDirectiveKind kind;
|
|
||||||
TxtPt pt;
|
|
||||||
String8 args;
|
|
||||||
union {
|
|
||||||
struct {
|
|
||||||
T_Compiler cc;
|
|
||||||
} compile;
|
|
||||||
};
|
|
||||||
} T_DbgScriptDirective;
|
|
||||||
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
U64 count;
|
|
||||||
T_DbgScriptDirective *first;
|
|
||||||
T_DbgScriptDirective *last;
|
|
||||||
} T_DbgScriptDirectiveList;
|
|
||||||
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
String8 file_path;
|
|
||||||
T_DbgScriptDirectiveList directives[OperatingSystem_COUNT][T_DbgScriptDirectiveKind_Count];
|
|
||||||
T_DbgScriptFileList files;
|
|
||||||
U64 program_count;
|
|
||||||
T_DbgScriptProgram **programs;
|
|
||||||
B32 skip;
|
|
||||||
} T_DbgScript;
|
|
||||||
|
|
||||||
|
|
||||||
////////////////////////////////
|
|
||||||
// IPC Controller
|
|
||||||
|
|
||||||
// error helper
|
|
||||||
internal void t_errorf_md(String8 file_name, String8 source, MD_Node *n, char *fmt, ...);
|
|
||||||
|
|
||||||
// reply parse helpers
|
|
||||||
internal B32 t_ipc_parse_string(MD_Node *node, String8 child_name, String8 *out);
|
|
||||||
internal B32 t_ipc_parse_u32(MD_Node *node, String8 child_name, U32 *out);
|
|
||||||
internal B32 t_ipc_parse_int_(MD_Node *node, String8 child_name, U64 out_size, void *out);
|
|
||||||
internal B32 t_ipc_parse_b32(MD_Node *node, String8 child_name, B32 *out);
|
|
||||||
#define t_ipc_parse_int(n, c, ptr) t_ipc_parse_int_(n, c, sizeof(*ptr), ptr)
|
|
||||||
|
|
||||||
// debugger commands
|
|
||||||
internal B32 t_dbg_send_cmd(String8 cmd, U64 timeout_us, Arena *reply_arena, MD_ParseResult *reply_out);
|
|
||||||
internal B32 t_dbg_send_cmdf(U64 timeout_us, Arena *reply_arena, MD_ParseResult *reply_out, char *fmt, ...);
|
|
||||||
internal T_DbgState * t_dbg_state(Arena *arena, U64 timeout_us);
|
|
||||||
internal B32 t_dbg_src_line(Arena *arena, U64 vaddr, T_DbgLineArray *lines_out, U64 timeout_us);
|
|
||||||
internal String8 t_dbg_value_from_expr(Arena *arena, String8 expr);
|
|
||||||
internal String8 t_dbg_value_from_exprf(Arena *arena, char *fmt, ...);
|
|
||||||
internal B32 t_dbg_send_cmd_and_wait_stop(String8 cmd, U64 timeout_us);
|
|
||||||
internal B32 t_dbg_ping(U64 timeout_us);
|
|
||||||
internal B32 t_dbg_bp_add_line(String8 file, U64 line);
|
|
||||||
internal B32 t_dbg_bp_add_func(String8 func_name);
|
|
||||||
internal B32 t_dbg_bp_add_addr(U64 addr);
|
|
||||||
internal B32 t_dbg_launch(String8 cmdline, U64 timeout_us);
|
|
||||||
internal B32 t_dbg_eval(Arena *arena, String8 expr, T_Eval *eval_out);
|
|
||||||
|
|
||||||
////////////////////////////////
|
|
||||||
// Dbg Script
|
|
||||||
|
|
||||||
internal String8 t_string_from_dbg_script_cmd_kind(T_DbgScriptCmdKind v);
|
|
||||||
internal T_DbgScriptCmdKind t_dbg_script_cmd_kind_from_string(String8 cmd);
|
|
||||||
internal B32 t_dbg_parse_script(Arena *arena, String8 file_path, String8 source, T_DbgScript *script_out);
|
|
||||||
internal B32 t_dbg_script_invoke(T_DbgScript *script, U64 timeout_us);
|
|
||||||
internal void t_dbg_register_script_tests(Arena *arena, String8 folder_path);
|
|
||||||
|
|
||||||
@@ -6,6 +6,7 @@
|
|||||||
|
|
||||||
#define BUILD_TITLE "TORTURE"
|
#define BUILD_TITLE "TORTURE"
|
||||||
|
|
||||||
|
#define BUILD_TESTS 1
|
||||||
#define BUILD_CONSOLE_INTERFACE 1
|
#define BUILD_CONSOLE_INTERFACE 1
|
||||||
#define OS_FEATURE_GRAPHICAL 1
|
#define OS_FEATURE_GRAPHICAL 1
|
||||||
#define DMN_INIT_MANUAL 1
|
#define DMN_INIT_MANUAL 1
|
||||||
@@ -94,8 +95,6 @@
|
|||||||
#include "linker/lnk_log.h"
|
#include "linker/lnk_log.h"
|
||||||
#include "linker/lnk_debug_helper.h"
|
#include "linker/lnk_debug_helper.h"
|
||||||
#include "torture.h"
|
#include "torture.h"
|
||||||
#include "torture_radlink.h"
|
|
||||||
#include "torture_dbg.h"
|
|
||||||
|
|
||||||
#include "base/base_inc.c"
|
#include "base/base_inc.c"
|
||||||
#include "x64/x64.c"
|
#include "x64/x64.c"
|
||||||
@@ -167,15 +166,15 @@
|
|||||||
#include "linker/pdb_ext/pdb_helpers.c"
|
#include "linker/pdb_ext/pdb_helpers.c"
|
||||||
#include "linker/pdb_ext/pdb_builder.c"
|
#include "linker/pdb_ext/pdb_builder.c"
|
||||||
#include "linker/lnk_debug_helper.c"
|
#include "linker/lnk_debug_helper.c"
|
||||||
|
|
||||||
#include "torture.c"
|
#include "torture.c"
|
||||||
#include "torture_base.c"
|
|
||||||
#include "torture_md.c"
|
#include "base/tests/base_tests.c"
|
||||||
#include "torture_radlink.c"
|
#include "mdesk/tests/mdesk_tests.c"
|
||||||
#include "torture_dwarf.c"
|
#include "linker/tests/linker_tests.c"
|
||||||
#include "torture_d2r.c"
|
#include "dwarf/tests/dwarf_tests.c"
|
||||||
#include "torture_p2r.c"
|
#include "rdi_from_dwarf/tests/rdi_from_dwarf_tests.c"
|
||||||
#include "torture_dbg.c"
|
#include "rdi_from_pdb/tests/rdi_from_pdb_tests.c"
|
||||||
|
#include "raddbg/tests/raddbg_tests.c"
|
||||||
|
|
||||||
internal B32 frame(void) { return 0; }
|
internal B32 frame(void) { return 0; }
|
||||||
|
|
||||||
|
|||||||
@@ -1,28 +0,0 @@
|
|||||||
// Copyright (c) Epic Games Tools
|
|
||||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
////////////////////////////////
|
|
||||||
|
|
||||||
typedef enum
|
|
||||||
{
|
|
||||||
T_MsvcLinkExitCode_UnresolvedExternals = 1120,
|
|
||||||
T_MsvcLinkExitCode_CorruptOrInvalidSymbolTable = 1235,
|
|
||||||
T_MsvcLinkExitCode_SectionsFoundWithDifferentAttributes = 4078,
|
|
||||||
} T_MsvcLinkExitCode;
|
|
||||||
|
|
||||||
////////////////////////////////
|
|
||||||
|
|
||||||
internal T_Linker t_id_linker(void);
|
|
||||||
|
|
||||||
internal COFF_ObjSection * t_push_text_section(COFF_ObjWriter *obj_writer, String8 data);
|
|
||||||
internal COFF_ObjSection * t_push_data_section(COFF_ObjWriter *obj_writer, String8 data);
|
|
||||||
internal COFF_ObjSection * t_push_rdata_section(COFF_ObjWriter *obj_writer, String8 data);
|
|
||||||
|
|
||||||
internal String8 t_make_entry_obj(Arena *arena);
|
|
||||||
internal String8 t_make_sec_defn_obj(Arena *arena, String8 payload);
|
|
||||||
internal String8 t_make_obj_with_directive(Arena *arena, String8 directive);
|
|
||||||
|
|
||||||
internal B32 t_write_entry_obj(void);
|
|
||||||
|
|
||||||
@@ -156,6 +156,16 @@ w32_thread_entry_point(void *ptr)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
//~ rjf: @per_os_impl Debugger Attachment Checking
|
||||||
|
|
||||||
|
internal B32
|
||||||
|
debugger_is_attached(void)
|
||||||
|
{
|
||||||
|
B32 result = IsDebuggerPresent();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
////////////////////////////////
|
////////////////////////////////
|
||||||
//~ rjf: @per_os_impl Platform Time Functions
|
//~ rjf: @per_os_impl Platform Time Functions
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user