__critical_log_child(main_pid, main_tid, log_id, prio, tag, fmt, ap);
}
+// LCOV_EXCL_START : stashing is essentially error handling.
static void stash_critical_inner(log_id_t log_id, log_priority prio, const char *tag, const char *fmt, ...)
{
va_list ap;
stash_critical_inner(log_id, prio, tag, "FAILED TO LOG: %s", msg);
return 0;
}
+// LCOV_EXCL_STOP
EXPORT_API int __dlog_critical_print(log_id_t log_id, int prio, const char *tag, const char *fmt, ...)
{
* (application preloader) closing all file descriptors explicitly
* (including libdlog's file descriptors) but still trying to log.
*/
+ // LCOV_EXCL_START : failure, nominally handled. No Sir, never actually happens in dlog.
int was_ebadf = (errno == EBADF);
if (!__reconnect_pipe(log_id))
return -1;
set_pipe_message_sent_timestamp((struct pipe_logger_entry *) buf, NULL, NULL);
ret = write(pipe_fd[log_id], buf, ple->len);
}
+ // LCOV_EXCL_STOP
}
return ret;
}
return LOG_ID_INVALID;
}
+// LCOV_EXCL_START : there's more error handling lines below than real ones here, I have a coverage goal to reach, and I am not spamming EXCL inside.
/**
* @brief Open an Android Logger device
* @param[in] fdi File descriptor info
return ret;
}
+// LCOV_EXCL_STOP
static void logger_destroy(struct fd_info *fdi)
{
char buff[LOG_MAX_PAYLOAD_SIZE + sizeof(struct android_logger_entry)];
if (lpd->monitor) {
+ // LCOV_EXCL_START : seems to be affected by the gcov rights bug, see logprint.c
assert(lpd->log_len != UNLIMITED_LOG_LEN);
while (lpd->log_len > 0) {
int r = read(fdi->fd, buff, sizeof buff);
}
lpd->log_len = UNLIMITED_LOG_LEN;
lpd->monitor = false;
+ // LCOV_EXCL_STOP
}
assert(!lpd->monitor);
return 0;
}
+// LCOV_EXCL_START : only for the zero-copy backend, which is not available on the coverage checking platform
static int how_long_would_we_have_to_wait(dlogutil_state_s *state)
{
/* Pick the smallest timeout. We're using epoll timeout
return 0;
}
+// LCOV_EXCL_STOP
static int refill_fdi_buffers(dlogutil_state_s *state, int timeout)
{
if (state->mode == DLOGUTIL_MODE_DUMP)
return;
+ // LCOV_EXCL_START : only weird error cases below.
+
/* The oldest log can be so fresh as to be from the future
* (i.e. has a negative age). This can happen when somebody
* changes the realtime clock backwards. In that case let
struct timespec reference_ts;
clock_gettime(get_proper_clock(state->logs.sort_by), &reference_ts);
state->flush_target = reference_ts.tv_sec * 1000 + reference_ts.tv_nsec / 1000000 - state->logs.timeout;
+
+ // LCOV_EXCL_STOP
}
static int get_timeout(dlogutil_state_s *state, int timeout, long last_log_age)
return 0;
}
+// LCOV_EXCL_START : coverage tests run on a platform without zero-copy logger.
+
static int apply_zero_copy_ioctl(int fd, const char *tag, log_priority prio)
{
int r = ioctl(fd, ZLOGGER_IOCTL_COMMAND_SET_DEFAULT_PRIORITY, (uint32_t)prio);
return 0;
}
+// LCOV_EXCL_STOP
+
int setup_single_unstructed(log_id_t buffer, const char *tag, log_priority prio, int *fd)
{
__attribute__((cleanup(log_config_free))) struct log_config config;
return -ENOKEY;
else if (!strcmp(backend, "logger"))
return setup_single_android_logger(buffer, tag, prio, &config, fd);
+
+ // LCOV_EXCL_START : coverage tests run on a platform with Android Logger
else if (!strcmp(backend, "pipe"))
return setup_single_pipe(buffer, tag, prio, &config, fd);
else if (!strcmp(backend, "zero-copy"))
return setup_single_null(buffer, tag, prio, &config, fd);
else
return -ENOTSUP;
+ // LCOV_EXCL_STOP
}
return is_buffer_valid(id) && buffer_traits[id].platform;
}
-bool is_extra_sink(log_id_t id)
-{
- return is_buffer_valid(id) && buffer_traits[id].extra_sink;
-}
-
char *log_name_by_id(log_id_t id)
{
return is_buffer_valid(id) ? buffer_traits[id].name : "";
return is_buffer_valid(id) ? buffer_traits[id].sink_redirect : LOG_ID_INVALID;
}
+// LCOV_EXCL_START : affected by the gcov rights bug, see logprint.c
+
+bool is_extra_sink(log_id_t id)
+{
+ return is_buffer_valid(id) && buffer_traits[id].extra_sink;
+}
+
+
log_id_t sink_id_by_name(const char *name)
{
for (log_id_t i = 0; i < SINKS_MAX; ++i)
return id;
}
+// LCOV_EXCL_STOP
+
&& data->result == DLOG_REQ_RESULT_OK;
}
+// LCOV_EXCL_START : most of the "common" functions seem to suffer from the gcov rights bug. See logprint.c
/**
* @brief Initialize message control for sending pipe fd
* @param[in] msg_hdr message control header to be populated with pipe fd to send
return 0;
}
+// LCOV_EXCL_STOP
/**
* @}
log_config_add(config, key, value);
}
+// LCOV_EXCL_START : never used, exists for API completeness
/**
* @brief Remove a config entry
* @details Removes entry with given key
return false;
}
+// LCOV_EXCL_STOP
static void log_config_read_file_by_handle(struct log_config *config, FILE *fp)
{
struct limiter_data *limiter_data;
};
+// LCOV_EXCL_START : coverage tests need to gather logs from non-dlog tests, so limiter is disabled.
void find_my_rule(elem_value value, void *userdata)
{
struct find_my_rule_data *find_my_rule_data = userdata;
return (r1->hash > r2->hash ? 1 : (-1));
}
+// LCOV_EXCL_STOP
static int rule_match(struct rule *r1, unsigned key, const char *s, int prio)
{
return 0;
}
+// LCOV_EXCL_START : only used by __dlog_fini, which is for internal state resets and not public API.
static void destroy_hashmap_etc(struct limiter_data *limiter_data)
{
assert(limiter_data);
destroy_hashmap_etc(limiter_data);
free(limiter_data);
}
+// LCOV_EXCL_STOP
struct rule *__log_limiter_add_rule(struct rule **rules_table, const char *tag, int prio, int limit)
{
return r;
}
-struct limiter_limits __log_limiter_get_limits(struct limiter_data *limiter_data, const char *tag, int prio)
-{
- const struct rule fallback = { .limit = -1 };
- const char prio_c = util_prio_to_char(prio);
-
- return (struct limiter_limits) {
- .tag_and_prio = (hashmap_search(limiter_data->rules_hashmap, tag, prio_c) ?: &fallback)->limit,
- .tag = (hashmap_search(limiter_data->rules_hashmap, tag, '*') ?: &fallback)->limit,
- .prio = (hashmap_search(limiter_data->rules_hashmap, "*", prio_c) ?: &fallback)->limit,
- .global = (hashmap_search(limiter_data->rules_hashmap, "*", '*') ?: &fallback)->limit,
- };
-}
-
-
/* Function implement logic needed to decide,
* whenever message is written to log or not.
*
limiter_data->current_rules_table = rules_table;
}
+// LCOV_EXCL_START : functions used by debugging/administration tools, not the public API.
+
+struct limiter_limits __log_limiter_get_limits(struct limiter_data *limiter_data, const char *tag, int prio)
+{
+ const struct rule fallback = { .limit = -1 };
+ const char prio_c = util_prio_to_char(prio);
+
+ return (struct limiter_limits) {
+ .tag_and_prio = (hashmap_search(limiter_data->rules_hashmap, tag, prio_c) ?: &fallback)->limit,
+ .tag = (hashmap_search(limiter_data->rules_hashmap, tag, '*') ?: &fallback)->limit,
+ .prio = (hashmap_search(limiter_data->rules_hashmap, "*", prio_c) ?: &fallback)->limit,
+ .global = (hashmap_search(limiter_data->rules_hashmap, "*", '*') ?: &fallback)->limit,
+ };
+}
+
/**
* @brief Dump a limiter rule to provided buffer
* @details Formats a string containing information about a rule and inserts it into provided buffer
{
return limiter_data->pid_rules;
}
+// LCOV_EXCL_STOP
return p_filter->global_pri;
}
+// LCOV_EXCL_START : gcov doesn't really work with system libs correctly.
+// It creates a file with whatever rights/etc the running binary has, so later
+// binaries using the same lib may fail to access them. This seems to happen
+// with dlog tests where we randomly get 0 coverage on lines that evidently
+// have run. The functions below are quite trivial anyway so no big deal.
+// Also note that this stupidity is pervasive so this comment is referenced in
+// other files as well, so be wary if you want to remove it.
+
/**
* @brief Print format from string
* @details Converts a string to print format
return returned;
}
+// LCOV_EXCL_STOP
+
int priority_from_filter_expression(const char *filterExpression, bool *exact, log_priority *pri)
{
assert(exact);
return list_add(&p_filter->filters, p_fi) ? TIZEN_ERROR_NONE : TIZEN_ERROR_OUT_OF_MEMORY;
}
+// LCOV_EXCL_START : same story as above.
+
dlogutil_sorting_order_e get_format_sorting(log_print_format format)
{
switch (format) {
return resolve_write(fd, wrbuf, buffer.buf_start, buffer.buf_position - buffer.buf_start);
}
+// LCOV_EXCL_STOP
+
/**
* @}
*/
return 0;
}
+// LCOV_EXCL_START : syslog parsers below, not currently supported
+
int parse_const_char(const char **cursor, void *user_data)
{
if (strlen(*cursor) <= 0 || **cursor != *((char *)user_data))
return parse(cursor, &mandatory_parsers);
}
+// LCOV_EXCL_STOP
+
/**
* @}
*/
return next;
}
+bool list_add(list_head *head, elem_value value)
+{
+ assert(head); //valid pointer
+
+ struct list_elem *elem = (struct list_elem *)calloc(1, sizeof(*elem));
+ if (elem == NULL)
+ return false;
+
+ *elem = (struct list_elem) {
+ .next = *head,
+ .value = value
+ };
+
+ (*head) = elem;
+ return true;
+}
+
+// LCOV_EXCL_START : these seem to suffer from the gcov rights bug. See logprint.c
/**
* @brief Check value equal to data given in user data
* @details elem value is passed by pointer
(*counter)++;
}
-bool list_add(list_head *head, elem_value value)
-{
- assert(head); //valid pointer
-
- struct list_elem *elem = (struct list_elem *)calloc(1, sizeof(*elem));
- if (elem == NULL)
- return false;
-
- *elem = (struct list_elem) {
- .next = *head,
- .value = value
- };
-
- (*head) = elem;
- return true;
-}
-
void list_remove(list_head *head, elem_value value)
{
list_remove_if(head, &value, cond_value_equal);
*head = list_remove_at(*head);
}
-void list_clear_free_contents(list_head *head)
-{
- if (!head)
- return;
-
- while (*head) {
- free(list_at(*head));
- *head = list_remove_at(*head);
- }
-}
-
-void list_clear_custom(list_head *head, void *user_data, apply_cb clear)
-{
- if (!head)
- return;
-
- while (*head) {
- clear(list_at(*head), user_data);
- *head = list_remove_at(*head);
- }
-}
void list_foreach(list_head head, void *user_data, apply_cb apply)
{
return ret;
}
+int list_count(list_head head)
+{
+ int counter = 0;
+ list_foreach(head, &counter, apply_increment_counter);
+ return counter;
+}
+// LCOV_EXCL_STOP
+
elem_value list_at(list_head iter)
{
return iter->value;
void list_next(list_head *iter)
{
- if (!*iter)
+ if (*iter)
+ *iter = (*iter)->next;
+}
+
+void list_clear_free_contents(list_head *head)
+{
+ if (!head)
return;
- *iter = (*iter)->next;
+ while (*head) {
+ free(list_at(*head));
+ *head = list_remove_at(*head);
+ }
}
-int list_count(list_head head)
+void list_clear_custom(list_head *head, void *user_data, apply_cb clear)
{
- int counter = 0;
- list_foreach(head, &counter, apply_increment_counter);
- return counter;
+ if (!head)
+ return;
+
+ while (*head) {
+ clear(list_at(*head), user_data);
+ *head = list_remove_at(*head);
+ }
}
ple->nsec_sent_real = real->tv_nsec;
}
-void fixup_pipe_msg(struct dlogutil_entry_with_msg *dewm, int payload_size)
-{
- assert(dewm);
- struct dlogutil_entry *const h = &dewm->header;
-
- /* For these IDs, 0 is treated as 'unknown'.
- * This already signifies that there was some
- * lost information and pipe-backend doesn't
- * have legitimate ways to achieve this otherwise
- * so we don't really need to explicitly say
- * that we've done this modification. */
- if (h->pid < 0)
- h->pid = 0;
- if (h->tid < 0)
- h->tid = 0;
-
-
- /* In theory we could try to just put INVALID_(N)SEC
- * in the timestamp and let the log through, but that
- * would break some of the assumptions made elsewhere
- * regarding what timestamps are always available in
- * which buffer for sorting purposes.
- *
- * This is more of a corruption check since nothing
- * prevents a program from, say, claiming it's been
- * sent in 1999 or three weeks into the future.
- *
- * We modify the log itself (as opposed to spawning
- * a new log) for the change notification because
- * doubling logs sounds like something exploitable
- * by malicious users. On the other hand, appending
- * stuff is fail-prone due to the log size limit.
- *
- * Ideally clients would just not let their own memory
- * get scribbled over, but logs *are* a debugging tool
- * that is supposed to let them get out of situations
- * like that when they happen so we should be lenient
- * in what we consider reasonable. */
- bool invalid_ts = false;
- if ((h->nsec_sent_mono < 0 && !(h->sec_sent_mono == INVALID_SEC && h->nsec_sent_mono == INVALID_NSEC)) || h->nsec_sent_mono > 999999999) {
- h->sec_sent_mono = h->sec_recv_mono;
- h->nsec_sent_mono = h->nsec_recv_mono;
- invalid_ts = true;
- }
- if ((h->nsec_sent_real < 0 && !(h->sec_sent_real == INVALID_SEC && h->nsec_sent_real == INVALID_NSEC)) || h->nsec_sent_real > 999999999) {
- h->sec_sent_real = h->sec_recv_real;
- h->nsec_sent_real = h->nsec_recv_real;
- invalid_ts = true;
- }
-
- #define WARN_MSG "\n[dlog inline warning: modified invalid timestamp]"
- if (invalid_ts && payload_size < sizeof dewm->msg) {
- const int available_space = sizeof dewm->msg - payload_size;
- strncpy(dewm->msg + payload_size - 1, WARN_MSG, available_space);
- const int added_size = available_space < sizeof WARN_MSG - 1 ?
- available_space:
- sizeof WARN_MSG - 1;
- payload_size += added_size;
- assert(payload_size <= sizeof dewm->msg);
- dewm->header.len += added_size;
- dewm->msg[payload_size - 1] = '\0';
- }
- #undef WARN_MSG
-}
-
+// LCOV_EXCL_START : no /dev/kmsg access from coverage tests
/**
* @brief Parse KMSG message
* @details Parses a raw KMSG message into the internal format
return 0;
}
+// LCOV_EXCL_STOP
/* Most of the logic between Android Logger and Pipe messages is similar and they
* happen to share field names, though this can't be a function because they are
le->nsec_sent_real = ale->nsec_sent;
}
+// LCOV_EXCL_START : used by the pipe daemon, who never really exits and thus never produces coverage reports.
void parse_pipe_message(struct pipe_logger_entry *ple, dlogutil_entry_s *le, size_t msg_size)
{
size_t payload_size = msg_size - sizeof *ple;
le->nsec_sent_real = ple->nsec_sent_real;
}
+void fixup_pipe_msg(struct dlogutil_entry_with_msg *dewm, int payload_size)
+{
+ assert(dewm);
+ struct dlogutil_entry *const h = &dewm->header;
+
+ /* For these IDs, 0 is treated as 'unknown'.
+ * This already signifies that there was some
+ * lost information and pipe-backend doesn't
+ * have legitimate ways to achieve this otherwise
+ * so we don't really need to explicitly say
+ * that we've done this modification. */
+ if (h->pid < 0)
+ h->pid = 0;
+ if (h->tid < 0)
+ h->tid = 0;
+
+
+ /* In theory we could try to just put INVALID_(N)SEC
+ * in the timestamp and let the log through, but that
+ * would break some of the assumptions made elsewhere
+ * regarding what timestamps are always available in
+ * which buffer for sorting purposes.
+ *
+ * This is more of a corruption check since nothing
+ * prevents a program from, say, claiming it's been
+ * sent in 1999 or three weeks into the future.
+ *
+ * We modify the log itself (as opposed to spawning
+ * a new log) for the change notification because
+ * doubling logs sounds like something exploitable
+ * by malicious users. On the other hand, appending
+ * stuff is fail-prone due to the log size limit.
+ *
+ * Ideally clients would just not let their own memory
+ * get scribbled over, but logs *are* a debugging tool
+ * that is supposed to let them get out of situations
+ * like that when they happen so we should be lenient
+ * in what we consider reasonable. */
+ bool invalid_ts = false;
+ if ((h->nsec_sent_mono < 0 && !(h->sec_sent_mono == INVALID_SEC && h->nsec_sent_mono == INVALID_NSEC)) || h->nsec_sent_mono > 999999999) {
+ h->sec_sent_mono = h->sec_recv_mono;
+ h->nsec_sent_mono = h->nsec_recv_mono;
+ invalid_ts = true;
+ }
+ if ((h->nsec_sent_real < 0 && !(h->sec_sent_real == INVALID_SEC && h->nsec_sent_real == INVALID_NSEC)) || h->nsec_sent_real > 999999999) {
+ h->sec_sent_real = h->sec_recv_real;
+ h->nsec_sent_real = h->nsec_recv_real;
+ invalid_ts = true;
+ }
+
+ #define WARN_MSG "\n[dlog inline warning: modified invalid timestamp]"
+ if (invalid_ts && payload_size < sizeof dewm->msg) {
+ const int available_space = sizeof dewm->msg - payload_size;
+ strncpy(dewm->msg + payload_size - 1, WARN_MSG, available_space);
+ const int added_size = available_space < sizeof WARN_MSG - 1 ?
+ available_space:
+ sizeof WARN_MSG - 1;
+ payload_size += added_size;
+ assert(payload_size <= sizeof dewm->msg);
+ dewm->header.len += added_size;
+ dewm->msg[payload_size - 1] = '\0';
+ }
+ #undef WARN_MSG
+}
+// LCOV_EXCL_STOP
+
+// LCOV_EXCL_START : syslog not currently supported
/**
* @brief Construct log from syslog datagram
* @details Constructs a logger entry from data contained in a syslog datagram.
return 0;
}
+// LCOV_EXCL_STOP
+// LCOV_EXCL_START : only used in internal tests. Will be refactored away into tests later.
/**
* @brief This function makes a deep copy of dlogutil_entry_s.
* @details In order to work properly dest, has to have sufficient allocated memory and source has to have a correct len field.
assert(source);
memcpy(dest, source, sizeof(dlogutil_entry_s)+source->len);
}
+// LCOV_EXCL_STOP
+
/**
* @}
*/
#include <queued_entry.h>
#include <queued_entry_timestamp.h>
+// LCOV_EXCL_START : `add` is not used by the Android Logger backend (present on the coverage gathering platform)
+// `copy` is only used in internal unit tests
/**
* @brief Reception timestamp
* @details Add a timestamp describing reception time.
le->sec_recv_real = le->sec_sent_real;
le->nsec_recv_real = le->nsec_sent_real;
}
+// LCOV_EXCL_STOP
bool log_entry_is_earlier(const dlogutil_sorting_order_e sort_by, const dlogutil_entry_s *lhs, const dlogutil_entry_s *rhs)
{