Renaming from McTest to DeepState.
This commit is contained in:
@@ -0,0 +1,273 @@
|
||||
/*
|
||||
* Copyright (c) 2017 Trail of Bits, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "deepstate/DeepState.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <limits.h>
|
||||
#include <setjmp.h>
|
||||
#include <stdio.h>
|
||||
|
||||
DEEPSTATE_BEGIN_EXTERN_C
|
||||
|
||||
/* Pointer to the last registers DeepState_TestInfo data structure */
|
||||
struct DeepState_TestInfo *DeepState_LastTestInfo = NULL;
|
||||
|
||||
enum {
|
||||
DeepState_InputLength = 8192
|
||||
};
|
||||
|
||||
/* Byte buffer that will contain symbolic data that is used to supply requests
|
||||
* for symbolic values (e.g. `int`s). */
|
||||
static volatile uint8_t DeepState_Input[DeepState_InputLength];
|
||||
|
||||
/* Index into the `DeepState_Input` array that tracks how many input bytes have
|
||||
* been consumed. */
|
||||
static uint32_t DeepState_InputIndex = 0;
|
||||
|
||||
/* Jump buffer for returning to `DeepState_Run`. */
|
||||
jmp_buf DeepState_ReturnToRun = {};
|
||||
|
||||
static const char *DeepState_TestAbandoned = NULL;
|
||||
static int DeepState_TestFailed = 0;
|
||||
|
||||
/* Abandon this test. We've hit some kind of internal problem. */
|
||||
DEEPSTATE_NORETURN
|
||||
void DeepState_Abandon(const char *reason) {
|
||||
DeepState_Log(DeepState_LogFatal, reason);
|
||||
DeepState_TestAbandoned = reason;
|
||||
longjmp(DeepState_ReturnToRun, 1);
|
||||
}
|
||||
|
||||
/* Mark this test as failing. */
|
||||
DEEPSTATE_NORETURN
|
||||
void DeepState_Fail(void) {
|
||||
DeepState_TestFailed = 1;
|
||||
longjmp(DeepState_ReturnToRun, 1);
|
||||
}
|
||||
|
||||
/* Mark this test as passing. */
|
||||
DEEPSTATE_NORETURN
|
||||
void DeepState_Pass(void) {
|
||||
longjmp(DeepState_ReturnToRun, 0);
|
||||
}
|
||||
|
||||
void DeepState_SoftFail(void) {
|
||||
DeepState_TestFailed = 1;
|
||||
}
|
||||
|
||||
/* Symbolize the data in the exclusive range `[begin, end)`. */
|
||||
void DeepState_SymbolizeData(void *begin, void *end) {
|
||||
uintptr_t begin_addr = (uintptr_t) begin;
|
||||
uintptr_t end_addr = (uintptr_t) end;
|
||||
|
||||
if (begin_addr > end_addr) {
|
||||
abort();
|
||||
} else if (begin_addr == end_addr) {
|
||||
return;
|
||||
} else {
|
||||
uint8_t *bytes = (uint8_t *) begin;
|
||||
for (uintptr_t i = 0, max_i = (end_addr - begin_addr); i < max_i; ++i) {
|
||||
bytes[i] = DeepState_Input[DeepState_InputIndex++];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Concretize some data in exclusive the range `[begin, end)`. */
|
||||
void *DeepState_ConcretizeData(void *begin, void *end) {
|
||||
return begin;
|
||||
}
|
||||
|
||||
/* Return a symbolic C string of length `len`. */
|
||||
char *DeepState_CStr(size_t len) {
|
||||
if (SIZE_MAX == len) {
|
||||
DeepState_Abandon("Can't create an SIZE_MAX-length string.");
|
||||
}
|
||||
char *str = (char *) malloc(sizeof(char) * (len + 1));
|
||||
if (len) {
|
||||
DeepState_SymbolizeData(str, &(str[len - 1]));
|
||||
}
|
||||
str[len] = '\0';
|
||||
return str;
|
||||
}
|
||||
|
||||
/* Concretize a C string */
|
||||
const char *DeepState_ConcretizeCStr(const char *begin) {
|
||||
return begin;
|
||||
}
|
||||
|
||||
/* Allocate and return a pointer to `num_bytes` symbolic bytes. */
|
||||
void *DeepState_Malloc(size_t num_bytes) {
|
||||
void *data = malloc(num_bytes);
|
||||
uintptr_t data_end = ((uintptr_t) data) + num_bytes;
|
||||
DeepState_SymbolizeData(data, (void *) data_end);
|
||||
return data;
|
||||
}
|
||||
|
||||
DEEPSTATE_NOINLINE int DeepState_One(void) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
DEEPSTATE_NOINLINE int DeepState_Zero(void) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Returns `1` if `expr` is true, and `0` otherwise. This is kind of an indirect
|
||||
* way to take a symbolic value, introduce a fork, and on each size, replace its
|
||||
* value with a concrete value. */
|
||||
int DeepState_IsTrue(int expr) {
|
||||
if (expr == DeepState_Zero()) {
|
||||
return DeepState_Zero();
|
||||
} else {
|
||||
return DeepState_One();
|
||||
}
|
||||
}
|
||||
|
||||
/* Return a symbolic value of a given type. */
|
||||
int DeepState_Bool(void) {
|
||||
return DeepState_Input[DeepState_InputIndex++] & 1;
|
||||
}
|
||||
|
||||
#define MAKE_SYMBOL_FUNC(Type, type) \
|
||||
type DeepState_ ## Type(void) { \
|
||||
type val = 0; \
|
||||
_Pragma("unroll") \
|
||||
for (size_t i = 0; i < sizeof(type); ++i) { \
|
||||
val = (val << 8) | ((type) DeepState_Input[DeepState_InputIndex++]); \
|
||||
} \
|
||||
return val; \
|
||||
}
|
||||
|
||||
|
||||
MAKE_SYMBOL_FUNC(Size, size_t)
|
||||
|
||||
MAKE_SYMBOL_FUNC(UInt64, uint64_t)
|
||||
int64_t DeepState_Int64(void) {
|
||||
return (int64_t) DeepState_UInt64();
|
||||
}
|
||||
|
||||
MAKE_SYMBOL_FUNC(UInt, uint32_t)
|
||||
int32_t DeepState_Int(void) {
|
||||
return (int32_t) DeepState_UInt();
|
||||
}
|
||||
|
||||
MAKE_SYMBOL_FUNC(UShort, uint16_t)
|
||||
int16_t DeepState_Short(void) {
|
||||
return (int16_t) DeepState_UShort();
|
||||
}
|
||||
|
||||
MAKE_SYMBOL_FUNC(UChar, uint8_t)
|
||||
int8_t DeepState_Char(void) {
|
||||
return (int8_t) DeepState_UChar();
|
||||
}
|
||||
|
||||
#undef MAKE_SYMBOL_FUNC
|
||||
|
||||
void _DeepState_Assume(int expr) {
|
||||
assert(expr);
|
||||
}
|
||||
|
||||
int DeepState_IsSymbolicUInt(uint32_t x) {
|
||||
(void) x;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Defined in Stream.c */
|
||||
extern void _DeepState_StreamInt(enum DeepState_LogLevel level, const char *format,
|
||||
const char *unpack, uint64_t *val);
|
||||
|
||||
extern void _DeepState_StreamFloat(enum DeepState_LogLevel level, const char *format,
|
||||
const char *unpack, double *val);
|
||||
|
||||
extern void _DeepState_StreamString(enum DeepState_LogLevel level, const char *format,
|
||||
const char *str);
|
||||
|
||||
/* A DeepState-specific symbol that is needed for hooking. */
|
||||
struct DeepState_IndexEntry {
|
||||
const char * const name;
|
||||
void * const address;
|
||||
};
|
||||
|
||||
/* An index of symbols that the symbolic executors will hook or
|
||||
* need access to. */
|
||||
const struct DeepState_IndexEntry DeepState_API[] = {
|
||||
|
||||
/* Control-flow during the test. */
|
||||
{"Pass", (void *) DeepState_Pass},
|
||||
{"Fail", (void *) DeepState_Fail},
|
||||
{"SoftFail", (void *) DeepState_SoftFail},
|
||||
{"Abandon", (void *) DeepState_Abandon},
|
||||
|
||||
/* Locating the tests. */
|
||||
{"LastTestInfo", (void *) &DeepState_LastTestInfo},
|
||||
|
||||
/* Source of symbolic bytes. */
|
||||
{"InputBegin", (void *) &(DeepState_Input[0])},
|
||||
{"InputEnd", (void *) &(DeepState_Input[DeepState_InputLength])},
|
||||
{"InputIndex", (void *) &DeepState_InputIndex},
|
||||
|
||||
/* Solver APIs. */
|
||||
{"Assume", (void *) _DeepState_Assume},
|
||||
{"IsSymbolicUInt", (void *) DeepState_IsSymbolicUInt},
|
||||
{"ConcretizeData", (void *) DeepState_ConcretizeData},
|
||||
{"ConcretizeCStr", (void *) DeepState_ConcretizeCStr},
|
||||
|
||||
/* Logging API. */
|
||||
{"Log", (void *) DeepState_Log},
|
||||
|
||||
/* Streaming API for deferred logging. */
|
||||
{"LogStream", (void *) DeepState_LogStream},
|
||||
{"StreamInt", (void *) _DeepState_StreamInt},
|
||||
{"StreamFloat", (void *) _DeepState_StreamFloat},
|
||||
{"StreamString", (void *) _DeepState_StreamString},
|
||||
|
||||
{NULL, NULL},
|
||||
};
|
||||
|
||||
/* Set up DeepState. */
|
||||
void DeepState_Setup(void) {
|
||||
/* TODO(pag): Sort the test cases by file name and line number. */
|
||||
}
|
||||
|
||||
/* Tear down DeepState. */
|
||||
void DeepState_Teardown(void) {
|
||||
|
||||
}
|
||||
|
||||
/* Notify that we're about to begin a test. */
|
||||
void DeepState_Begin(struct DeepState_TestInfo *info) {
|
||||
DeepState_TestFailed = 0;
|
||||
DeepState_TestAbandoned = NULL;
|
||||
DeepState_LogFormat(DeepState_LogInfo, "Running: %s from %s(%u)",
|
||||
info->test_name, info->file_name, info->line_number);
|
||||
}
|
||||
|
||||
/* Return the first test case to run. */
|
||||
struct DeepState_TestInfo *DeepState_FirstTest(void) {
|
||||
return DeepState_LastTestInfo;
|
||||
}
|
||||
|
||||
/* Returns 1 if a failure was caught, otherwise 0. */
|
||||
int DeepState_CatchFail(void) {
|
||||
return DeepState_TestFailed;
|
||||
}
|
||||
|
||||
/* Returns 1 if this test case was abandoned. */
|
||||
int DeepState_CatchAbandoned(void) {
|
||||
return DeepState_TestAbandoned != NULL;
|
||||
}
|
||||
|
||||
DEEPSTATE_END_EXTERN_C
|
||||
+36
-36
@@ -18,22 +18,22 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <mctest/McTest.h>
|
||||
#include "deepstate/DeepState.h"
|
||||
|
||||
MCTEST_BEGIN_EXTERN_C
|
||||
DEEPSTATE_BEGIN_EXTERN_C
|
||||
|
||||
/* Returns a printable string version of the log level. */
|
||||
static const char *McTest_LogLevelStr(enum McTest_LogLevel level) {
|
||||
static const char *DeepState_LogLevelStr(enum DeepState_LogLevel level) {
|
||||
switch (level) {
|
||||
case McTest_LogDebug:
|
||||
case DeepState_LogDebug:
|
||||
return "DEBUG";
|
||||
case McTest_LogInfo:
|
||||
case DeepState_LogInfo:
|
||||
return "INFO";
|
||||
case McTest_LogWarning:
|
||||
case DeepState_LogWarning:
|
||||
return "WARNING";
|
||||
case McTest_LogError:
|
||||
case DeepState_LogError:
|
||||
return "ERROR";
|
||||
case McTest_LogFatal:
|
||||
case DeepState_LogFatal:
|
||||
return "FATAL";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
@@ -41,69 +41,69 @@ static const char *McTest_LogLevelStr(enum McTest_LogLevel level) {
|
||||
}
|
||||
|
||||
enum {
|
||||
McTest_LogBufSize = 4096
|
||||
DeepState_LogBufSize = 4096
|
||||
};
|
||||
|
||||
char McTest_LogBuf[McTest_LogBufSize + 1] = {};
|
||||
char DeepState_LogBuf[DeepState_LogBufSize + 1] = {};
|
||||
|
||||
/* Log a C string. */
|
||||
void McTest_Log(enum McTest_LogLevel level, const char *str) {
|
||||
memset(McTest_LogBuf, 0, McTest_LogBufSize);
|
||||
snprintf(McTest_LogBuf, McTest_LogBufSize, "%s: %s",
|
||||
McTest_LogLevelStr(level), str);
|
||||
fputs(McTest_LogBuf, stderr);
|
||||
void DeepState_Log(enum DeepState_LogLevel level, const char *str) {
|
||||
memset(DeepState_LogBuf, 0, DeepState_LogBufSize);
|
||||
snprintf(DeepState_LogBuf, DeepState_LogBufSize, "%s: %s",
|
||||
DeepState_LogLevelStr(level), str);
|
||||
fputs(DeepState_LogBuf, stderr);
|
||||
|
||||
if (McTest_LogError == level) {
|
||||
McTest_SoftFail();
|
||||
} else if (McTest_LogFatal == level) {
|
||||
McTest_Fail();
|
||||
if (DeepState_LogError == level) {
|
||||
DeepState_SoftFail();
|
||||
} else if (DeepState_LogFatal == level) {
|
||||
DeepState_Fail();
|
||||
}
|
||||
}
|
||||
|
||||
/* Log some formatted output. */
|
||||
void McTest_LogFormat(enum McTest_LogLevel level, const char *format, ...) {
|
||||
McTest_LogStream(level);
|
||||
void DeepState_LogFormat(enum DeepState_LogLevel level, const char *format, ...) {
|
||||
DeepState_LogStream(level);
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
McTest_StreamVFormat(level, format, args);
|
||||
DeepState_StreamVFormat(level, format, args);
|
||||
va_end(args);
|
||||
McTest_LogStream(level);
|
||||
DeepState_LogStream(level);
|
||||
}
|
||||
|
||||
/* Log some formatted output. */
|
||||
void McTest_LogVFormat(enum McTest_LogLevel level,
|
||||
void DeepState_LogVFormat(enum DeepState_LogLevel level,
|
||||
const char *format, va_list args) {
|
||||
McTest_LogStream(level);
|
||||
McTest_StreamVFormat(level, format, args);
|
||||
McTest_LogStream(level);
|
||||
DeepState_LogStream(level);
|
||||
DeepState_StreamVFormat(level, format, args);
|
||||
DeepState_LogStream(level);
|
||||
}
|
||||
|
||||
/* Override libc! */
|
||||
int printf(const char *format, ...) {
|
||||
McTest_LogStream(McTest_LogInfo);
|
||||
DeepState_LogStream(DeepState_LogInfo);
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
McTest_StreamVFormat(McTest_LogInfo, format, args);
|
||||
DeepState_StreamVFormat(DeepState_LogInfo, format, args);
|
||||
va_end(args);
|
||||
McTest_LogStream(McTest_LogInfo);
|
||||
DeepState_LogStream(DeepState_LogInfo);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fprintf(FILE *file, const char *format, ...) {
|
||||
enum McTest_LogLevel level = McTest_LogInfo;
|
||||
enum DeepState_LogLevel level = DeepState_LogInfo;
|
||||
if (stderr == file) {
|
||||
level = McTest_LogDebug;
|
||||
level = DeepState_LogDebug;
|
||||
} else if (stdout != file) {
|
||||
return 0; /* TODO(pag): This is probably evil. */
|
||||
}
|
||||
|
||||
McTest_LogStream(level);
|
||||
DeepState_LogStream(level);
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
McTest_StreamVFormat(level, format, args);
|
||||
DeepState_StreamVFormat(level, format, args);
|
||||
va_end(args);
|
||||
McTest_LogStream(level);
|
||||
DeepState_LogStream(level);
|
||||
return 0;
|
||||
}
|
||||
|
||||
MCTEST_END_EXTERN_C
|
||||
DEEPSTATE_END_EXTERN_C
|
||||
|
||||
@@ -1,251 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2017 Trail of Bits, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <mctest/Compiler.h>
|
||||
#include <mctest/McTest.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <setjmp.h>
|
||||
#include <stdio.h>
|
||||
|
||||
MCTEST_BEGIN_EXTERN_C
|
||||
|
||||
/* Pointer to the last registers McTest_TestInfo data structure */
|
||||
struct McTest_TestInfo *McTest_LastTestInfo = NULL;
|
||||
|
||||
enum {
|
||||
McTest_InputLength = 8192
|
||||
};
|
||||
|
||||
/* Byte buffer that will contain symbolic data that is used to supply requests
|
||||
* for symbolic values (e.g. `int`s). */
|
||||
static volatile uint8_t McTest_Input[McTest_InputLength];
|
||||
|
||||
/* Index into the `McTest_Input` array that tracks how many input bytes have
|
||||
* been consumed. */
|
||||
static uint32_t McTest_InputIndex = 0;
|
||||
|
||||
/* Jump buffer for returning to `McTest_Run`. */
|
||||
jmp_buf McTest_ReturnToRun = {};
|
||||
|
||||
static const char *McTest_TestAbandoned = NULL;
|
||||
static int McTest_TestFailed = 0;
|
||||
|
||||
/* Abandon this test. We've hit some kind of internal problem. */
|
||||
MCTEST_NORETURN
|
||||
void McTest_Abandon(const char *reason) {
|
||||
McTest_Log(McTest_LogFatal, reason);
|
||||
McTest_TestAbandoned = reason;
|
||||
longjmp(McTest_ReturnToRun, 1);
|
||||
}
|
||||
|
||||
/* Mark this test as failing. */
|
||||
MCTEST_NORETURN
|
||||
void McTest_Fail(void) {
|
||||
McTest_TestFailed = 1;
|
||||
longjmp(McTest_ReturnToRun, 1);
|
||||
}
|
||||
|
||||
/* Mark this test as passing. */
|
||||
MCTEST_NORETURN
|
||||
void McTest_Pass(void) {
|
||||
longjmp(McTest_ReturnToRun, 0);
|
||||
}
|
||||
|
||||
void McTest_SoftFail(void) {
|
||||
McTest_TestFailed = 1;
|
||||
}
|
||||
|
||||
void McTest_SymbolizeData(void *begin, void *end) {
|
||||
uintptr_t begin_addr = (uintptr_t) begin;
|
||||
uintptr_t end_addr = (uintptr_t) end;
|
||||
|
||||
if (begin_addr > end_addr) {
|
||||
abort();
|
||||
} else if (begin_addr == end_addr) {
|
||||
return;
|
||||
} else {
|
||||
uint8_t *bytes = (uint8_t *) begin;
|
||||
for (uintptr_t i = 0, max_i = (end_addr - begin_addr); i < max_i; ++i) {
|
||||
bytes[i] = McTest_Input[McTest_InputIndex++];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Concretize some data i nthe range `[begin, end)`. */
|
||||
void McTest_ConcretizeData(void *begin, void *end) {
|
||||
(void) begin;
|
||||
}
|
||||
|
||||
/* Concretize a C string */
|
||||
void McTest_ConcretizeCStr(const char *begin) {
|
||||
(void) begin;
|
||||
}
|
||||
|
||||
MCTEST_NOINLINE int McTest_One(void) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
MCTEST_NOINLINE int McTest_Zero(void) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Returns `1` if `expr` is true, and `0` otherwise. This is kind of an indirect
|
||||
* way to take a symbolic value, introduce a fork, and on each size, replace its
|
||||
* value with a concrete value. */
|
||||
int McTest_IsTrue(int expr) {
|
||||
if (expr == McTest_Zero()) {
|
||||
return McTest_Zero();
|
||||
} else {
|
||||
return McTest_One();
|
||||
}
|
||||
}
|
||||
|
||||
/* Return a symbolic value of a given type. */
|
||||
int McTest_Bool(void) {
|
||||
return McTest_Input[McTest_InputIndex++] & 1;
|
||||
}
|
||||
|
||||
#define MAKE_SYMBOL_FUNC(Type, type) \
|
||||
type McTest_ ## Type(void) { \
|
||||
type val = 0; \
|
||||
_Pragma("unroll") \
|
||||
for (size_t i = 0; i < sizeof(type); ++i) { \
|
||||
val = (val << 8) | ((type) McTest_Input[McTest_InputIndex++]); \
|
||||
} \
|
||||
return val; \
|
||||
}
|
||||
|
||||
|
||||
MAKE_SYMBOL_FUNC(Size, size_t)
|
||||
|
||||
MAKE_SYMBOL_FUNC(UInt64, uint64_t)
|
||||
int64_t McTest_Int64(void) {
|
||||
return (int64_t) McTest_UInt64();
|
||||
}
|
||||
|
||||
MAKE_SYMBOL_FUNC(UInt, uint32_t)
|
||||
int32_t McTest_Int(void) {
|
||||
return (int32_t) McTest_UInt();
|
||||
}
|
||||
|
||||
MAKE_SYMBOL_FUNC(UShort, uint16_t)
|
||||
int16_t McTest_Short(void) {
|
||||
return (int16_t) McTest_UShort();
|
||||
}
|
||||
|
||||
MAKE_SYMBOL_FUNC(UChar, uint8_t)
|
||||
int8_t McTest_Char(void) {
|
||||
return (int8_t) McTest_UChar();
|
||||
}
|
||||
|
||||
#undef MAKE_SYMBOL_FUNC
|
||||
|
||||
void _McTest_Assume(int expr) {
|
||||
assert(expr);
|
||||
}
|
||||
|
||||
int McTest_IsSymbolicUInt(uint32_t x) {
|
||||
(void) x;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Defined in Stream.c */
|
||||
extern void _McTest_StreamInt(enum McTest_LogLevel level, const char *format,
|
||||
const char *unpack, uint64_t *val);
|
||||
|
||||
extern void _McTest_StreamFloat(enum McTest_LogLevel level, const char *format,
|
||||
const char *unpack, double *val);
|
||||
|
||||
extern void _McTest_StreamString(enum McTest_LogLevel level, const char *format,
|
||||
const char *str);
|
||||
|
||||
/* A McTest-specific symbol that is needed for hooking. */
|
||||
struct McTest_IndexEntry {
|
||||
const char * const name;
|
||||
void * const address;
|
||||
};
|
||||
|
||||
/* An index of symbols that the symbolic executors will hook or
|
||||
* need access to. */
|
||||
const struct McTest_IndexEntry McTest_API[] = {
|
||||
|
||||
/* Control-flow during the test. */
|
||||
{"Pass", (void *) McTest_Pass},
|
||||
{"Fail", (void *) McTest_Fail},
|
||||
{"SoftFail", (void *) McTest_SoftFail},
|
||||
{"Abandon", (void *) McTest_Abandon},
|
||||
|
||||
/* Locating the tests. */
|
||||
{"LastTestInfo", (void *) &McTest_LastTestInfo},
|
||||
|
||||
/* Source of symbolic bytes. */
|
||||
{"InputBegin", (void *) &(McTest_Input[0])},
|
||||
{"InputEnd", (void *) &(McTest_Input[McTest_InputLength])},
|
||||
{"InputIndex", (void *) &McTest_InputIndex},
|
||||
|
||||
/* Solver APIs. */
|
||||
{"Assume", (void *) _McTest_Assume},
|
||||
{"IsSymbolicUInt", (void *) McTest_IsSymbolicUInt},
|
||||
{"ConcretizeData", (void *) McTest_ConcretizeData},
|
||||
{"ConcretizeCStr", (void *) McTest_ConcretizeCStr},
|
||||
|
||||
/* Logging API. */
|
||||
{"Log", (void *) McTest_Log},
|
||||
|
||||
/* Streaming API for deferred logging. */
|
||||
{"LogStream", (void *) McTest_LogStream},
|
||||
{"StreamInt", (void *) _McTest_StreamInt},
|
||||
{"StreamFloat", (void *) _McTest_StreamFloat},
|
||||
{"StreamString", (void *) _McTest_StreamString},
|
||||
|
||||
{NULL, NULL},
|
||||
};
|
||||
|
||||
/* Set up McTest. */
|
||||
void McTest_Setup(void) {
|
||||
/* TODO(pag): Sort the test cases by file name and line number. */
|
||||
}
|
||||
|
||||
/* Tear down McTest. */
|
||||
void McTest_Teardown(void) {
|
||||
|
||||
}
|
||||
|
||||
/* Notify that we're about to begin a test. */
|
||||
void McTest_Begin(struct McTest_TestInfo *info) {
|
||||
McTest_TestFailed = 0;
|
||||
McTest_TestAbandoned = NULL;
|
||||
McTest_LogFormat(McTest_LogInfo, "Running: %s from %s(%u)",
|
||||
info->test_name, info->file_name, info->line_number);
|
||||
}
|
||||
|
||||
/* Return the first test case to run. */
|
||||
struct McTest_TestInfo *McTest_FirstTest(void) {
|
||||
return McTest_LastTestInfo;
|
||||
}
|
||||
|
||||
/* Returns 1 if a failure was caught, otherwise 0. */
|
||||
int McTest_CatchFail(void) {
|
||||
return McTest_TestFailed;
|
||||
}
|
||||
|
||||
/* Returns 1 if this test case was abandoned. */
|
||||
int McTest_CatchAbandoned(void) {
|
||||
return McTest_TestAbandoned != NULL;
|
||||
}
|
||||
|
||||
MCTEST_END_EXTERN_C
|
||||
+112
-89
@@ -22,17 +22,16 @@
|
||||
#include <string.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#include <mctest/Compiler.h>
|
||||
#include <mctest/McTest.h>
|
||||
#include "deepstate/DeepState.h"
|
||||
|
||||
MCTEST_BEGIN_EXTERN_C
|
||||
DEEPSTATE_BEGIN_EXTERN_C
|
||||
|
||||
enum {
|
||||
McTest_StreamSize = 4096
|
||||
DeepState_StreamSize = 4096
|
||||
};
|
||||
|
||||
/* Formatting options availale to the streaming API. */
|
||||
struct McTest_StreamFormatOptions {
|
||||
struct DeepState_StreamFormatOptions {
|
||||
/* int radix; */
|
||||
int hex;
|
||||
int oct;
|
||||
@@ -47,10 +46,10 @@ struct McTest_StreamFormatOptions {
|
||||
* mirrors C++ I/O streams, not because I/O streams are good, but instead
|
||||
* because the ability to stream in data to things like the C++-backed
|
||||
* `ASSERT` and `CHECK` macros is really nice. */
|
||||
struct McTest_Stream {
|
||||
struct DeepState_Stream {
|
||||
int size;
|
||||
struct McTest_StreamFormatOptions options;
|
||||
char message[McTest_StreamSize + 2];
|
||||
struct DeepState_StreamFormatOptions options;
|
||||
char message[DeepState_StreamSize + 2];
|
||||
char staging[32];
|
||||
char format[32];
|
||||
char unpack[32];
|
||||
@@ -61,28 +60,35 @@ struct McTest_Stream {
|
||||
};
|
||||
|
||||
/* Hard-coded streams for each log level. */
|
||||
static struct McTest_Stream McTest_Streams[McTest_LogFatal + 1] = {};
|
||||
static struct DeepState_Stream DeepState_Streams[DeepState_LogFatal + 1] = {};
|
||||
|
||||
static char McTest_EndianSpecifier = '=';
|
||||
/* Endian specifier for Python's `struct.pack` and `struct.unpack`.
|
||||
* = Native endian
|
||||
* < Little endian
|
||||
* > Big endian
|
||||
*/
|
||||
static char DeepState_EndianSpecifier = '=';
|
||||
|
||||
/* Figure out what the Python `struct` endianness specifier should be. */
|
||||
MCTEST_INITIALIZER(DetectEndianness) {
|
||||
DEEPSTATE_INITIALIZER(DetectEndianness) {
|
||||
static const int one = 1;
|
||||
if ((const char *) &one) {
|
||||
McTest_EndianSpecifier = '<'; /* Little endian. */
|
||||
DeepState_EndianSpecifier = '<'; /* Little endian. */
|
||||
} else {
|
||||
McTest_EndianSpecifier = '>'; /* Big endian. */
|
||||
DeepState_EndianSpecifier = '>'; /* Big endian. */
|
||||
}
|
||||
}
|
||||
|
||||
static void McTest_StreamUnpack(struct McTest_Stream *stream, char type) {
|
||||
stream->unpack[0] = McTest_EndianSpecifier;
|
||||
/* Fills the `stream->unpack` character buffer with a Python `struct.unpack`-
|
||||
* compatible format specifier. */
|
||||
static void DeepState_StreamUnpack(struct DeepState_Stream *stream, char type) {
|
||||
stream->unpack[0] = DeepState_EndianSpecifier;
|
||||
stream->unpack[1] = type;
|
||||
stream->unpack[2] = '\0';
|
||||
}
|
||||
|
||||
/* Fill in the format for when we want to stream an integer. */
|
||||
static void McTest_StreamIntFormat(struct McTest_Stream *stream,
|
||||
static void DeepState_StreamIntFormat(struct DeepState_Stream *stream,
|
||||
size_t val_size, int is_unsigned) {
|
||||
char *format = stream->format;
|
||||
int i = 0;
|
||||
@@ -135,11 +141,13 @@ static void McTest_StreamIntFormat(struct McTest_Stream *stream,
|
||||
format[i++] = '\0';
|
||||
}
|
||||
|
||||
static void CheckCapacity(struct McTest_Stream *stream, int num_chars_to_add) {
|
||||
/* Make sure that we don't exceed our formatting capacity when running. */
|
||||
static void CheckCapacity(struct DeepState_Stream *stream,
|
||||
int num_chars_to_add) {
|
||||
if (0 > num_chars_to_add) {
|
||||
McTest_Abandon("Can't add a negative number of characters to a stream.");
|
||||
} else if ((stream->size + num_chars_to_add) >= McTest_StreamSize) {
|
||||
McTest_Abandon("Exceeded capacity of stream buffer.");
|
||||
DeepState_Abandon("Can't add a negative number of characters to a stream.");
|
||||
} else if ((stream->size + num_chars_to_add) >= DeepState_StreamSize) {
|
||||
DeepState_Abandon("Exceeded capacity of stream buffer.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -147,12 +155,12 @@ static void CheckCapacity(struct McTest_Stream *stream, int num_chars_to_add) {
|
||||
* be hooked by the symbolic executor, so that it can easily pull out the
|
||||
* relevant data from `*val`, which may be symbolic, and defer the actual
|
||||
* formatting until later. */
|
||||
MCTEST_NOINLINE
|
||||
void _McTest_StreamInt(enum McTest_LogLevel level, const char *format,
|
||||
DEEPSTATE_NOINLINE
|
||||
void _DeepState_StreamInt(enum DeepState_LogLevel level, const char *format,
|
||||
const char *unpack, uint64_t *val) {
|
||||
struct McTest_Stream *stream = &(McTest_Streams[level]);
|
||||
struct DeepState_Stream *stream = &(DeepState_Streams[level]);
|
||||
int size = 0;
|
||||
int remaining_size = McTest_StreamSize - stream->size;
|
||||
int remaining_size = DeepState_StreamSize - stream->size;
|
||||
if (unpack[1] == 'Q' || unpack[1] == 'q') {
|
||||
size = snprintf(&(stream->message[stream->size]),
|
||||
remaining_size, format, *val);
|
||||
@@ -164,22 +172,24 @@ void _McTest_StreamInt(enum McTest_LogLevel level, const char *format,
|
||||
stream->size += size;
|
||||
}
|
||||
|
||||
MCTEST_NOINLINE
|
||||
void _McTest_StreamFloat(enum McTest_LogLevel level, const char *format,
|
||||
/* Format a streamed-in float. This gets hooked. */
|
||||
DEEPSTATE_NOINLINE
|
||||
void _DeepState_StreamFloat(enum DeepState_LogLevel level, const char *format,
|
||||
const char *unpack, double *val) {
|
||||
struct McTest_Stream *stream = &(McTest_Streams[level]);
|
||||
int remaining_size = McTest_StreamSize - stream->size;
|
||||
struct DeepState_Stream *stream = &(DeepState_Streams[level]);
|
||||
int remaining_size = DeepState_StreamSize - stream->size;
|
||||
int size = snprintf(&(stream->message[stream->size]),
|
||||
remaining_size, format, *val);
|
||||
CheckCapacity(stream, size);
|
||||
stream->size += size;
|
||||
}
|
||||
|
||||
MCTEST_NOINLINE
|
||||
void _McTest_StreamString(enum McTest_LogLevel level, const char *format,
|
||||
/* Format a streamed-in NUL-terminated string. This gets hooked. */
|
||||
DEEPSTATE_NOINLINE
|
||||
void _DeepState_StreamString(enum DeepState_LogLevel level, const char *format,
|
||||
const char *str) {
|
||||
struct McTest_Stream *stream = &(McTest_Streams[level]);
|
||||
int remaining_size = McTest_StreamSize - stream->size;
|
||||
struct DeepState_Stream *stream = &(DeepState_Streams[level]);
|
||||
int remaining_size = DeepState_StreamSize - stream->size;
|
||||
int size = snprintf(&(stream->message[stream->size]),
|
||||
remaining_size, format, str);
|
||||
CheckCapacity(stream, size);
|
||||
@@ -187,13 +197,13 @@ void _McTest_StreamString(enum McTest_LogLevel level, const char *format,
|
||||
}
|
||||
|
||||
#define MAKE_INT_STREAMER(Type, type, is_unsigned, pack_kind) \
|
||||
void McTest_Stream ## Type(enum McTest_LogLevel level, type val) { \
|
||||
struct McTest_Stream *stream = &(McTest_Streams[level]); \
|
||||
McTest_StreamIntFormat(stream, sizeof(val), is_unsigned); \
|
||||
McTest_StreamUnpack(stream, pack_kind); \
|
||||
void DeepState_Stream ## Type(enum DeepState_LogLevel level, type val) { \
|
||||
struct DeepState_Stream *stream = &(DeepState_Streams[level]); \
|
||||
DeepState_StreamIntFormat(stream, sizeof(val), is_unsigned); \
|
||||
DeepState_StreamUnpack(stream, pack_kind); \
|
||||
stream->value.as_uint64 = (uint64_t) val; \
|
||||
_McTest_StreamInt(level, stream->format, stream->unpack, \
|
||||
&(stream->value.as_uint64)); \
|
||||
_DeepState_StreamInt(level, stream->format, stream->unpack, \
|
||||
&(stream->value.as_uint64)); \
|
||||
}
|
||||
|
||||
MAKE_INT_STREAMER(Pointer, void *, 1, (sizeof(void *) == 8 ? 'Q' : 'I'))
|
||||
@@ -213,16 +223,16 @@ MAKE_INT_STREAMER(Int8, int8_t, 0, 'c')
|
||||
#undef MAKE_INT_STREAMER
|
||||
|
||||
/* Stream a C string into the stream's message. */
|
||||
void McTest_StreamCStr(enum McTest_LogLevel level, const char *begin) {
|
||||
_McTest_StreamString(level, "%s", begin);
|
||||
void DeepState_StreamCStr(enum DeepState_LogLevel level, const char *begin) {
|
||||
_DeepState_StreamString(level, "%s", begin);
|
||||
}
|
||||
|
||||
/* Stream a some data in the inclusive range `[begin, end]` into the
|
||||
* stream's message. */
|
||||
/*void McTest_StreamData(enum McTest_LogLevel level, const void *begin,
|
||||
/*void DeepState_StreamData(enum DeepState_LogLevel level, const void *begin,
|
||||
const void *end) {
|
||||
struct McTest_Stream *stream = &(McTest_Streams[level]);
|
||||
int remaining_size = McTest_StreamSize - stream->size;
|
||||
struct DeepState_Stream *stream = &(DeepState_Streams[level]);
|
||||
int remaining_size = DeepState_StreamSize - stream->size;
|
||||
int input_size = (int) ((uintptr_t) end - (uintptr_t) begin) + 1;
|
||||
CheckCapacity(stream, input_size);
|
||||
memcpy(&(stream->message[stream->size]), begin, (size_t) input_size);
|
||||
@@ -233,38 +243,45 @@ void McTest_StreamCStr(enum McTest_LogLevel level, const char *begin) {
|
||||
* be hooked by the symbolic executor, so that it can easily pull out the
|
||||
* relevant data from `*val`, which may be symbolic, and defer the actual
|
||||
* formatting until later. */
|
||||
void McTest_StreamDouble(enum McTest_LogLevel level, double val) {
|
||||
struct McTest_Stream *stream = &(McTest_Streams[level]);
|
||||
void DeepState_StreamDouble(enum DeepState_LogLevel level, double val) {
|
||||
struct DeepState_Stream *stream = &(DeepState_Streams[level]);
|
||||
const char *format = "%f"; /* TODO(pag): Support more? */
|
||||
stream->value.as_fp64 = val;
|
||||
McTest_StreamUnpack(stream, 'd');
|
||||
_McTest_StreamFloat(level, format, stream->unpack, &(stream->value.as_fp64));
|
||||
DeepState_StreamUnpack(stream, 'd');
|
||||
_DeepState_StreamFloat(level, format, stream->unpack, &(stream->value.as_fp64));
|
||||
}
|
||||
|
||||
/* Flush the contents of the stream to a log. */
|
||||
void McTest_LogStream(enum McTest_LogLevel level) {
|
||||
struct McTest_Stream *stream = &(McTest_Streams[level]);
|
||||
void DeepState_LogStream(enum DeepState_LogLevel level) {
|
||||
struct DeepState_Stream *stream = &(DeepState_Streams[level]);
|
||||
if (stream->size) {
|
||||
stream->message[stream->size] = '\n';
|
||||
stream->message[stream->size + 1] = '\0';
|
||||
stream->message[McTest_StreamSize] = '\0';
|
||||
McTest_Log(level, stream->message);
|
||||
memset(stream->message, 0, McTest_StreamSize);
|
||||
stream->message[DeepState_StreamSize] = '\0';
|
||||
DeepState_Log(level, stream->message);
|
||||
memset(stream->message, 0, DeepState_StreamSize);
|
||||
stream->size = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Reset the formatting in a stream. */
|
||||
void McTest_StreamResetFormatting(enum McTest_LogLevel level) {
|
||||
struct McTest_Stream *stream = &(McTest_Streams[level]);
|
||||
void DeepState_StreamResetFormatting(enum DeepState_LogLevel level) {
|
||||
struct DeepState_Stream *stream = &(DeepState_Streams[level]);
|
||||
memset(&(stream->options), 0, sizeof(stream->options));
|
||||
}
|
||||
|
||||
static int McTest_NumLsInt64BitFormat = 2;
|
||||
|
||||
/* `PRId64` will be "ld" or "lld" */
|
||||
DEEPSTATE_INITIALIZER(McTest_NumLsFor64BitFormat) {
|
||||
McTest_NumLsInt64BitFormat = (PRId64)[1] == 'd' ? 1 : 2;
|
||||
}
|
||||
|
||||
/* Approximately do string format parsing and convert it into calls into our
|
||||
* streaming API. */
|
||||
static int McTest_StreamFormatValue(enum McTest_LogLevel level,
|
||||
const char *format, va_list args) {
|
||||
struct McTest_Stream *stream = &(McTest_Streams[level]);
|
||||
static int DeepState_StreamFormatValue(enum DeepState_LogLevel level,
|
||||
const char *format, va_list args) {
|
||||
struct DeepState_Stream *stream = &(DeepState_Streams[level]);
|
||||
char format_buf[32] = {'\0'};
|
||||
int i = 0;
|
||||
int k = 0;
|
||||
@@ -274,6 +291,7 @@ static int McTest_StreamFormatValue(enum McTest_LogLevel level,
|
||||
int is_unsigned = 0;
|
||||
int is_float = 0;
|
||||
int long_double = 0;
|
||||
int num_ls = 0;
|
||||
char extract = '\0';
|
||||
|
||||
#define READ_FORMAT_CHAR \
|
||||
@@ -284,7 +302,7 @@ static int McTest_StreamFormatValue(enum McTest_LogLevel level,
|
||||
READ_FORMAT_CHAR; /* Read the '%' */
|
||||
|
||||
if ('%' != ch) {
|
||||
McTest_Abandon("Invalid format.");
|
||||
DeepState_Abandon("Invalid format.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -293,7 +311,7 @@ get_flag_char:
|
||||
READ_FORMAT_CHAR;
|
||||
switch (ch) {
|
||||
case '\0':
|
||||
McTest_Abandon("Incomplete format (flags).");
|
||||
DeepState_Abandon("Incomplete format (flags).");
|
||||
return 0;
|
||||
case '-':
|
||||
case '+':
|
||||
@@ -309,10 +327,10 @@ get_flag_char:
|
||||
get_width_char:
|
||||
switch (ch) {
|
||||
case '\0':
|
||||
McTest_Abandon("Incomplete format (width).");
|
||||
DeepState_Abandon("Incomplete format (width).");
|
||||
return 0;
|
||||
case '*':
|
||||
McTest_Abandon("Variable width printing not supported.");
|
||||
DeepState_Abandon("Variable width printing not supported.");
|
||||
return 0;
|
||||
case '0':
|
||||
case '1':
|
||||
@@ -336,10 +354,10 @@ get_width_char:
|
||||
READ_FORMAT_CHAR;
|
||||
switch (ch) {
|
||||
case '\0':
|
||||
McTest_Abandon("Incomplete format (precision).");
|
||||
DeepState_Abandon("Incomplete format (precision).");
|
||||
return 0;
|
||||
case '*':
|
||||
McTest_Abandon("Variable precision printing not supported.");
|
||||
DeepState_Abandon("Variable precision printing not supported.");
|
||||
break;
|
||||
case '0':
|
||||
case '1':
|
||||
@@ -361,7 +379,7 @@ get_width_char:
|
||||
get_length_char:
|
||||
switch (ch) {
|
||||
case '\0':
|
||||
McTest_Abandon("Incomplete format (length).");
|
||||
DeepState_Abandon("Incomplete format (length).");
|
||||
return 0;
|
||||
case 'L':
|
||||
long_double = 1;
|
||||
@@ -373,7 +391,7 @@ get_length_char:
|
||||
READ_FORMAT_CHAR;
|
||||
goto get_length_char;
|
||||
case 'l':
|
||||
length *= 2;
|
||||
num_ls += 1;
|
||||
READ_FORMAT_CHAR;
|
||||
goto get_length_char;
|
||||
case 'j':
|
||||
@@ -394,7 +412,7 @@ get_length_char:
|
||||
|
||||
if (!length) {
|
||||
length = 1;
|
||||
} else if (8 < length) {
|
||||
} else if (num_ls >= McTest_NumLsInt64BitFormat) {
|
||||
length = 8;
|
||||
}
|
||||
|
||||
@@ -403,7 +421,7 @@ get_length_char:
|
||||
/* Specifier */
|
||||
switch(ch) {
|
||||
case '\0':
|
||||
McTest_Abandon("Incomplete format (specifier).");
|
||||
DeepState_Abandon("Incomplete format (specifier).");
|
||||
return 0;
|
||||
|
||||
case 'n':
|
||||
@@ -431,7 +449,7 @@ get_length_char:
|
||||
stream->value.as_uint64 = (uint64_t) va_arg(args, int64_t);
|
||||
extract = 'q';
|
||||
} else {
|
||||
McTest_Abandon("Unsupported integer length.");
|
||||
DeepState_Abandon("Unsupported integer length.");
|
||||
}
|
||||
goto common_stream_int;
|
||||
|
||||
@@ -459,12 +477,12 @@ get_length_char:
|
||||
stream->value.as_uint64 = (uint64_t) va_arg(args, uint64_t);
|
||||
extract = 'Q';
|
||||
} else {
|
||||
McTest_Abandon("Unsupported integer length.");
|
||||
DeepState_Abandon("Unsupported integer length.");
|
||||
}
|
||||
|
||||
common_stream_int:
|
||||
McTest_StreamUnpack(stream, extract);
|
||||
_McTest_StreamInt(level, format_buf, stream->unpack,
|
||||
DeepState_StreamUnpack(stream, extract);
|
||||
_DeepState_StreamInt(level, format_buf, stream->unpack,
|
||||
&(stream->value.as_uint64));
|
||||
break;
|
||||
|
||||
@@ -482,37 +500,42 @@ get_length_char:
|
||||
} else {
|
||||
stream->value.as_fp64 = va_arg(args, double);
|
||||
}
|
||||
McTest_StreamUnpack(stream, 'd');
|
||||
DeepState_StreamUnpack(stream, 'd');
|
||||
break;
|
||||
|
||||
case 's':
|
||||
_McTest_StreamString(level, format_buf, va_arg(args, const char *));
|
||||
_DeepState_StreamString(level, format_buf, va_arg(args, const char *));
|
||||
break;
|
||||
|
||||
default:
|
||||
McTest_Abandon("Unsupported format specifier.");
|
||||
DeepState_Abandon("Unsupported format specifier.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
static char McTest_Format[McTest_StreamSize + 1];
|
||||
/* Holding buffer for a format string. If we have something like `foo%dbar`
|
||||
* then we want to be able to pull out the `%d`, and so having the format
|
||||
* string in a mutable buffer lets us conveniently NUL-out the `b` of `bar`
|
||||
* following the `%d`. */
|
||||
static char DeepState_Format[DeepState_StreamSize + 1];
|
||||
|
||||
/* Stream some formatted input */
|
||||
void McTest_StreamVFormat(enum McTest_LogLevel level, const char *format_,
|
||||
va_list args) {
|
||||
/* Stream some formatted input. This converts a `printf`-style format string
|
||||
* into a */
|
||||
void DeepState_StreamVFormat(enum DeepState_LogLevel level, const char *format_,
|
||||
va_list args) {
|
||||
char *begin = NULL;
|
||||
char *end = NULL;
|
||||
char *format = McTest_Format;
|
||||
char *format = DeepState_Format;
|
||||
int i = 0;
|
||||
char ch = '\0';
|
||||
char next_ch = '\0';
|
||||
|
||||
strncpy(format, format_, McTest_StreamSize);
|
||||
format[McTest_StreamSize] = '\0';
|
||||
strncpy(format, format_, DeepState_StreamSize);
|
||||
format[DeepState_StreamSize] = '\0';
|
||||
|
||||
McTest_ConcretizeCStr(format);
|
||||
DeepState_ConcretizeCStr(format);
|
||||
|
||||
for (i = 0; '\0' != (ch = format[i]); ) {
|
||||
if (!begin) {
|
||||
@@ -524,7 +547,7 @@ void McTest_StreamVFormat(enum McTest_LogLevel level, const char *format_,
|
||||
end = &(format[i]);
|
||||
next_ch = end[1];
|
||||
end[1] = '\0';
|
||||
McTest_StreamCStr(level, begin);
|
||||
DeepState_StreamCStr(level, begin);
|
||||
end[1] = next_ch;
|
||||
begin = NULL;
|
||||
end = NULL;
|
||||
@@ -534,12 +557,12 @@ void McTest_StreamVFormat(enum McTest_LogLevel level, const char *format_,
|
||||
if (end) {
|
||||
next_ch = end[1];
|
||||
end[1] = '\0';
|
||||
McTest_StreamCStr(level, begin);
|
||||
DeepState_StreamCStr(level, begin);
|
||||
end[1] = next_ch;
|
||||
}
|
||||
begin = NULL;
|
||||
end = NULL;
|
||||
i += McTest_StreamFormatValue(level, &(format[i]), args);
|
||||
i += DeepState_StreamFormatValue(level, &(format[i]), args);
|
||||
}
|
||||
} else {
|
||||
end = &(format[i]);
|
||||
@@ -548,16 +571,16 @@ void McTest_StreamVFormat(enum McTest_LogLevel level, const char *format_,
|
||||
}
|
||||
|
||||
if (begin && begin[0]) {
|
||||
McTest_StreamCStr(level, begin);
|
||||
DeepState_StreamCStr(level, begin);
|
||||
}
|
||||
}
|
||||
|
||||
/* Stream some formatted input */
|
||||
void McTest_StreamFormat(enum McTest_LogLevel level, const char *format, ...) {
|
||||
void DeepState_StreamFormat(enum DeepState_LogLevel level, const char *format, ...) {
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
McTest_StreamVFormat(level, format, args);
|
||||
DeepState_StreamVFormat(level, format, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
MCTEST_END_EXTERN_C
|
||||
DEEPSTATE_END_EXTERN_C
|
||||
|
||||
Reference in New Issue
Block a user