return len;
}
+#ifndef UNIT_TEST
+__attribute__ ((noreturn))
+#endif
void __critical_log_child(pid_t main_pid, pid_t main_tid, log_id_t log_id, int prio, const char *tag, const char *fmt, va_list ap)
{
char buf[LOG_MAX_PAYLOAD_SIZE + 128]; // extra space for some metadata
execl(path, path /* argv[0] convention */, buf, (char *) NULL);
#ifndef UNIT_TEST
- /* Compilers are sometimes smart enough to recognize `_exit`
- * and can put some code behind the call that makes absolutely
- * sure the program dies. This seems to happen even in case
- * `_exit` is replaced with something that doesn't actually
- * abort the program, so we can't use `_exit` in tests. */
+ /* Compilers are sometimes smart enough to recognize _exit's
+ * noreturn attribute, even if we wrap it with something that
+ * returns. This causes it to behave in unexpected ways, for
+ * example it can blow up the program regardless or it can
+ * optimize some conditionals out (and incorrectly enter them
+ * after the exit call fails to actually exit). This makes it
+ * unsuitable for tests. */
_exit(1); // not the regular `exit` so as not to trigger any `atexit` handlers prematurely
#endif
}
+#ifndef UNIT_TEST // contains forks and exits, these don't work well with wrapping (see above)
void __critical_log(log_id_t log_id, int prio, const char *tag, const char *fmt, va_list ap)
{
/* Critical log functionality is mostly done in a separate binary
__critical_log_child(main_pid, main_tid, log_id, prio, tag, fmt, ap);
}
+int __dlog_critical_print(log_id_t log_id, int prio, const char *tag, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ __critical_log(log_id, prio, tag, fmt, ap);
+ va_end(ap);
+
+ va_start(ap, fmt);
+ int ret = __dlog_vprint(log_id, prio, tag, fmt, ap);
+ va_end(ap);
+
+ return ret;
+}
+#endif
+
/**
* @brief Print log
* @details Print a log line
return ret;
}
-int __dlog_critical_print(log_id_t log_id, int prio, const char *tag, const char *fmt, ...)
-{
- va_list ap;
-
- va_start(ap, fmt);
- __critical_log(log_id, prio, tag, fmt, ap);
- va_end(ap);
-
- va_start(ap, fmt);
- int ret = __dlog_vprint(log_id, prio, tag, fmt, ap);
- va_end(ap);
-
- return ret;
-}
-
int dlog_vprint(log_priority prio, const char *tag, const char *fmt, va_list ap)
{
return __write_to_log(LOG_ID_APPS, prio, tag, fmt, ap, false);
#include <assert.h>
#include <string.h>
-bool fake_fork = false;
-pid_t fork_ret;
-pid_t __real_fork(void);
-pid_t __wrap_fork(void)
-{
- return fake_fork ? fork_ret : __real_fork();
-}
-
void __dlog_init_pipe(const struct log_config *conf) { }
void __dlog_init_android(const struct log_config *conf) { }
return 0;
}
-long __wrap_syscall(long number, long arg)
-{
- assert(number == SYS_gettid);
- return 2077;
-}
-
-pid_t __real_getpid();
-pid_t __wrap_getpid()
-{
- return 1729;
-}
-
int __wrap_clock_gettime(clockid_t clk_id, struct timespec *tp)
{
if (clk_id == CLOCK_REALTIME) {
return 0;
}
+// the real version does forks/exits plus regular logging, these are wonky when wrapped so we skip them
+void __critical_log_child(pid_t main_pid, pid_t main_tid, log_id_t log_id, int prio, const char *tag, const char *fmt, va_list ap);
+int __dlog_critical_print_emulated(log_id_t log_id, int prio, const char *tag, const char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ __critical_log_child(1729, 2077, log_id, prio, tag, fmt, ap);
+ va_end(ap);
+ return 0;
+}
+
bool called_atexit;
void atexit_f(void)
{
int main()
{
- fake_fork = true;
called_atexit = false;
assert(!atexit(atexit_f));
- fork_ret = -1;
- __dlog_critical_print(LOG_ID_MAIN, DLOG_ERROR, "TAG", "%s", "YOU'RE IT");
- assert(!executed);
-
- fork_ret = 17;
- __dlog_critical_print(LOG_ID_MAIN, DLOG_ERROR, "MONTAG", "%s%d", "F", 451);
- assert(!executed);
-
- fork_ret = 0;
message_ok = false;
- __dlog_critical_print(LOG_ID_MAIN, DLOG_ERROR, "FHTAG", "PHNGLUI MGLW'NAFH");
+ __dlog_critical_print_emulated(LOG_ID_MAIN, DLOG_ERROR, "FHTAG", "PHNGLUI MGLW'NAFH");
assert(executed);
assert(message_ok);
// No content checks needed, it just needs not to explode
executed = false;
- __dlog_critical_print(LOG_ID_MAIN, DLOG_ERROR, big_str, "stuff");
+ __dlog_critical_print_emulated(LOG_ID_MAIN, DLOG_ERROR, big_str, "stuff");
assert(executed);
executed = false;
- __dlog_critical_print(LOG_ID_MAIN, DLOG_ERROR, "stuff", big_str);
+ __dlog_critical_print_emulated(LOG_ID_MAIN, DLOG_ERROR, "stuff", big_str);
assert(executed);
assert(!called_atexit);
- fake_fork = false;
return 0;
}