From: Michal Bloch Date: Tue, 26 Apr 2022 14:56:22 +0000 (+0200) Subject: In-memory compressed log backup X-Git-Tag: accepted/tizen/unified/20220525.134532~3 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=refs%2Fchanges%2F70%2F271770%2F9;p=platform%2Fcore%2Fsystem%2Fdlog.git In-memory compressed log backup Change-Id: I87a94908152fab5808a1a6cd4791a171ad225aea --- diff --git a/Makefile.am b/Makefile.am index d85a108..3712a9f 100644 --- a/Makefile.am +++ b/Makefile.am @@ -126,19 +126,23 @@ dlog_logger_LDFLAGS = \ dlog_logger_SOURCES = \ external/sd-daemon/sd-daemon.c \ + external/fastlz/fastlz.c \ src/logger/logger.c \ src/logger/logger_privileges.c \ src/logger/dlogutil_line.c \ src/logger/fd_entity.c \ src/logger/log_buffer.c \ src/logger/log_storage.c \ + src/logger/log_compressed_storage.c \ src/logger/qos.c \ src/logger/qos_distributions.c \ src/logger/reader_common.c \ src/logger/reader_logger.c \ src/logger/reader_pipe.c \ + src/logger/reader_memory.c \ src/logger/subreader_dlogutil.c \ src/logger/subreader_file.c \ + src/logger/subreader_memory.c \ src/logger/subreader_metrics.c \ src/logger/socket.c \ src/logger/writer.c \ diff --git a/include/dlogutil-internal.h b/include/dlogutil-internal.h index b4e84d6..894d1fa 100644 --- a/include/dlogutil-internal.h +++ b/include/dlogutil-internal.h @@ -34,6 +34,7 @@ typedef enum { DLOGUTIL_MODE_CONTINUOUS, DLOGUTIL_MODE_DUMP, DLOGUTIL_MODE_MONITOR, + DLOGUTIL_MODE_COMPRESSED_MEMORY_DUMP, } dlogutil_mode_e; typedef enum { @@ -53,6 +54,7 @@ struct dlogutil_config { unsigned int buffers; unsigned int dump_size; dlogutil_mode_e mode; + char *compress; }; #ifdef __cplusplus diff --git a/include/dlogutil.h b/include/dlogutil.h index f5d472f..f5a7d58 100644 --- a/include/dlogutil.h +++ b/include/dlogutil.h @@ -305,6 +305,7 @@ void dlogutil_state_destroy(dlogutil_state_s *state); * @retval TIZEN_ERROR_INVALID_PARAMETER Config was NULL * @see dlogutil_config_mode_set_monitor() * @see dlogutil_config_mode_set_dump() + * @see dlogutil_config_mode_set_compressed_memory_dump() */ int dlogutil_config_mode_set_continuous(dlogutil_config_s *config); @@ -318,6 +319,7 @@ int dlogutil_config_mode_set_continuous(dlogutil_config_s *config); * @retval TIZEN_ERROR_INVALID_PARAMETER Config was NULL * @see dlogutil_config_mode_set_continuous() * @see dlogutil_config_mode_set_dump() + * @see dlogutil_config_mode_set_compressed_memory_dump() */ int dlogutil_config_mode_set_monitor(dlogutil_config_s *config); @@ -334,9 +336,26 @@ int dlogutil_config_mode_set_monitor(dlogutil_config_s *config); * @retval TIZEN_ERROR_INVALID_PARAMETER Config was NULL * @see dlogutil_config_mode_set_continuous() * @see dlogutil_config_mode_set_monitor() + * @see dlogutil_config_mode_set_compressed_memory_dump() */ int dlogutil_config_mode_set_dump(dlogutil_config_s *config, unsigned int entry_count); +/** + * @brief Set log retrieval mode to dumping compressed historical logs + * @since_tizen 7.0 + * @remarks This is similar to `cat /var/log/dlog/xyz`. After dumping all the logs, dlogutil_get_log() will signal this by returning TIZEN_ERROR_NO_DATA. + * @param[in,out] config The configuration struct + * @param[in] compression_buffer The name of the compression storage entry + * @return An error code + * @retval TIZEN_ERROR_NONE Success + * @retval TIZEN_ERROR_OUT_OF_MEMORY Not enough memory. Parameters left unchanged + * @retval TIZEN_ERROR_INVALID_PARAMETER Config or compression_buffer was NULL + * @see dlogutil_config_mode_set_continuous() + * @see dlogutil_config_mode_set_monitor() + * @see dlogutil_config_mode_set_dump() + */ +int dlogutil_config_mode_set_compressed_memory_dump(dlogutil_config_s *config, const char *compression_buffer); + /** * @brief Finalizes the config into a state struct by connecting to buffers diff --git a/include/logpipe.h b/include/logpipe.h index ad99b88..7ca8da4 100644 --- a/include/logpipe.h +++ b/include/logpipe.h @@ -46,6 +46,7 @@ enum { DLOG_REQ_STDOUT = 6 << 1, DLOG_REQ_GLOBAL_ENABLE_DISABLE_STDOUT = 7 << 1, DLOG_REQ_GET_STDOUT = 8 << 1, + DLOG_REQ_HANDLE_COMPRESSED_LOGUTIL = 9 << 1, }; enum { diff --git a/include/util_parser.h b/include/util_parser.h index 991e819..ee894ff 100644 --- a/include/util_parser.h +++ b/include/util_parser.h @@ -47,6 +47,7 @@ struct parse_result { dlogutil_mode_e mode; /**< Log printing mode */ unsigned dump_size; /**< If dump mode is enabled, number of logs to be dumped, or #DLOGUTIL_MAX_DUMP_SIZE if infinite */ dlogutil_sorting_order_e sort_by; /**< The timestamp type to be sorted on */ + char *compression; /**< The compression storage name */ }; const char *which_option; /**< An statically allocated string meaning an option, including the initial dash(es) */ const char *bad_contents; /**< An string from input argument which has been not a valid argument */ diff --git a/src/libdlogutil/fd_info.h b/src/libdlogutil/fd_info.h index c391ff0..7753f4a 100644 --- a/src/libdlogutil/fd_info.h +++ b/src/libdlogutil/fd_info.h @@ -60,7 +60,7 @@ struct fd_ops { /// Transition into a state that allows reading and printing out logs /// Return values > 0 mean everything went fine but we don't want to print - int (*prepare_print)(struct fd_info *fdi, int dump, bool monitor, struct log_filter *filter_object); + int (*prepare_print)(struct fd_info *fdi, int dump, bool monitor, struct log_filter *filter_object, char *compress); /// Clear the buffer int (*clear)(struct fd_info *fdi); diff --git a/src/libdlogutil/fdi_logger.c b/src/libdlogutil/fdi_logger.c index 57dd2ad..092b719 100644 --- a/src/libdlogutil/fdi_logger.c +++ b/src/libdlogutil/fdi_logger.c @@ -173,12 +173,13 @@ static void logger_destroy(struct fd_info *fdi) fdi->priv_data = NULL; } -static int logger_prepare_print(struct fd_info *fdi, int dump, bool monitor, struct log_filter *filter_object) +static int logger_prepare_print(struct fd_info *fdi, int dump, bool monitor, struct log_filter *filter_object, char *compress) { assert(fdi); struct logger_priv_data *const lpd = (struct logger_priv_data *)fdi->priv_data; assert(lpd); assert(filter_object); + assert(!compress); lpd->monitor = monitor; diff --git a/src/libdlogutil/fdi_pipe.c b/src/libdlogutil/fdi_pipe.c index c410953..1bf13fb 100644 --- a/src/libdlogutil/fdi_pipe.c +++ b/src/libdlogutil/fdi_pipe.c @@ -138,20 +138,22 @@ size_t get_filter_size(struct log_filter *filters) } /** - * @brief Send a logger request - * @details Send a logging request to the daemon + * @brief Send a standard logger request + * @details Send a standard (not compressed memory) logging request to the daemon * @param[in] filters Log filters to use * @param[in] dump Whether dumping or not * @param[in] sock_fd Socket file descriptor * @return 0 on success, -errno on failure */ -static int send_logger_request(struct log_filter *filters, int dump, bool monitor, int sock_fd) +static int send_logger_request(struct log_filter *filters, int dump, bool monitor, int sock_fd, char *compress) { assert(filters); assert(sock_fd >= 0); assert(!dump || !monitor); + static const char compress_opt_str[] = " --compress "; size_t request_string_len = get_filter_size(filters) + + (compress ? (strlen(compress) + sizeof compress_opt_str - 1) : 0) + (dump ? 3 : 0) + // " -d" (monitor ? 3 : 0); // " -m" @@ -176,6 +178,14 @@ static int send_logger_request(struct log_filter *filters, int dump, bool monito len += sizeof monitor_str - 1; } + if (compress) { + strncat(request_string, compress_opt_str, request_string_len - len); + len += sizeof compress_opt_str - 1; + + strncat(request_string, compress, request_string_len - len); + len += strlen(compress); + } + int needed = 0; for (list_head iter = log_filter_get_list(filters); iter; list_next(&iter)) { @@ -219,7 +229,7 @@ static int send_logger_request(struct log_filter *filters, int dump, bool monito len += needed; request_string[len - 1] = '\0'; - return send_dlog_request(sock_fd, DLOG_REQ_HANDLE_LOGUTIL, request_string, len); + return send_dlog_request(sock_fd, compress ? DLOG_REQ_HANDLE_COMPRESSED_LOGUTIL : DLOG_REQ_HANDLE_LOGUTIL, request_string, len); } /** @@ -273,7 +283,17 @@ static void pipe_destroy(struct fd_info *fdi) fdi->priv_data = NULL; } -static int pipe_prepare_print(struct fd_info *fdi, int dump, bool monitor, struct log_filter *filter_object) +/* NB: compressed logs are handled almost exactly the same as regular logs, + * with only the setup being slightly different (see below). This is because + * the decompression is done entirely in the daemon (so the logs we receive + * are already decompressed and look the same as usual). + * + * The potential downside of this design would usually be that the daemon + * is critical so it would be better to relegate the load (so as not to + * delay incoming logs) but the memory logs are only supposed to be dumped + * manually and quite rarely so performance isn't a worry, while this + * lets us keep compression and decompression in the same executable. */ +static int pipe_prepare_print(struct fd_info *fdi, int dump, bool monitor, struct log_filter *filter_object, char *compress) { assert(filter_object); assert(fdi); @@ -281,7 +301,7 @@ static int pipe_prepare_print(struct fd_info *fdi, int dump, bool monitor, struc assert(ppd); assert(ppd->sock_fd >= 0); - int r = send_logger_request(filter_object, dump, monitor, ppd->sock_fd); + int r = send_logger_request(filter_object, dump, monitor, ppd->sock_fd, compress); if (r < 0) return r; diff --git a/src/libdlogutil/lib.c b/src/libdlogutil/lib.c index ae92cf9..96645f5 100644 --- a/src/libdlogutil/lib.c +++ b/src/libdlogutil/lib.c @@ -175,7 +175,7 @@ EXPORT_API int dlogutil_config_connect(dlogutil_config_s *config, dlogutil_state log_id_t aliased[LOG_ID_MAX]; for (int i = 0; i < LOG_ID_MAX; ++i) aliased[i] = LOG_ID_INVALID; - int fdi_cnt = create_initial_fdis(&fdi_ptrs, config->buffers, is_pipe, &conf, aliased); + int fdi_cnt = create_initial_fdis(&fdi_ptrs, config->buffers, is_pipe, config->mode == DLOGUTIL_MODE_COMPRESSED_MEMORY_DUMP, &conf, aliased); if (fdi_cnt < 0) return TIZEN_ERROR_IO_ERROR; if (fdi_cnt == 0) @@ -225,6 +225,22 @@ EXPORT_API int dlogutil_config_mode_set_dump(dlogutil_config_s *config, unsigned return TIZEN_ERROR_NONE; } +EXPORT_API int dlogutil_config_mode_set_compressed_memory_dump(dlogutil_config_s *config, const char *compress_buffer) +{ + CHECK_PARAM(config); + CHECK_PARAM(compress_buffer); + + char *copy = strdup(compress_buffer); + if (!copy) + return TIZEN_ERROR_OUT_OF_MEMORY; + + free(config->compress); + config->compress = copy; + config->mode = DLOGUTIL_MODE_COMPRESSED_MEMORY_DUMP; + + return TIZEN_ERROR_NONE; +} + EXPORT_API int dlogutil_get_log(dlogutil_state_s *state, int timeout, dlogutil_entry_s **entry_out) { CHECK_PARAM(state); diff --git a/src/libdlogutil/logretrieve.c b/src/libdlogutil/logretrieve.c index ffb11be..842488b 100644 --- a/src/libdlogutil/logretrieve.c +++ b/src/libdlogutil/logretrieve.c @@ -38,7 +38,7 @@ bool all_buffers_known_disabled(const struct log_config *conf, int buffers) && !log_config_get_boolean(conf, "handle_kmsg", true); } -int create_initial_fdis(struct fd_info ***fdis, int enabled_buffers, bool is_pipe, const struct log_config *conf, log_id_t aliased[LOG_ID_MAX]) +int create_initial_fdis(struct fd_info ***fdis, int enabled_buffers, bool is_pipe, bool is_compressed_memory, const struct log_config *conf, log_id_t aliased[LOG_ID_MAX]) { assert(fdis); assert(conf); @@ -76,14 +76,21 @@ int create_initial_fdis(struct fd_info ***fdis, int enabled_buffers, bool is_pip continue; struct fd_info *fdi; - switch (i) { - case LOG_ID_KMSG: - case LOG_ID_SYSLOG: + if (is_compressed_memory) { + /* Ignore `is_pipe`. This feature is done + * via pipes always, even on the Android + * Logger backend. */ fdi = fdi_create(&ops_pipe, i); - break; - default: - fdi = fdi_create(is_pipe ? &ops_pipe : &ops_logger, i); - break; + } else { + switch (i) { + case LOG_ID_KMSG: + case LOG_ID_SYSLOG: + fdi = fdi_create(&ops_pipe, i); + break; + default: + fdi = fdi_create(is_pipe ? &ops_pipe : &ops_logger, i); + break; + } } if (!fdi) return -ENOMEM; @@ -171,12 +178,12 @@ int put_logs_into_vector(struct fd_info **data_fds, int fd_count, struct sort_ve return TIZEN_ERROR_NONE; } -static int state_prepare_single_print(dlogutil_state_s *state, int nfds) +static int state_prepare_single_print(dlogutil_state_s *state, int nfds, char *compress) { assert(state); assert(state->mode != DLOGUTIL_MODE_NONPRINTING); - int r = state->data_fds[nfds]->ops->prepare_print(state->data_fds[nfds], state->dump_size, state->mode == DLOGUTIL_MODE_MONITOR, state->filter_object); + int r = state->data_fds[nfds]->ops->prepare_print(state->data_fds[nfds], state->dump_size, state->mode == DLOGUTIL_MODE_MONITOR, state->filter_object, compress); if (r < 0) return TIZEN_ERROR_IO_ERROR; if (r > 0) { @@ -201,7 +208,7 @@ static int state_prepare_single_print(dlogutil_state_s *state, int nfds) return TIZEN_ERROR_NONE; } -static int state_prepare_prints(dlogutil_state_s *state) +static int state_prepare_prints(dlogutil_state_s *state, char *compress) { assert(state); assert(state->mode != DLOGUTIL_MODE_NONPRINTING); @@ -211,7 +218,7 @@ static int state_prepare_prints(dlogutil_state_s *state) return TIZEN_ERROR_OUT_OF_MEMORY; for (int nfds = 0; nfds < state->fd_count; ++nfds) { - int r = state_prepare_single_print(state, nfds); + int r = state_prepare_single_print(state, nfds, compress); if (r != TIZEN_ERROR_NONE) return r; } @@ -268,7 +275,7 @@ int dlogutil_state_init(dlogutil_state_s *state, struct fd_info ***data_fds_ptr, return TIZEN_ERROR_OUT_OF_MEMORY; if (state->mode != DLOGUTIL_MODE_NONPRINTING) { - int r = state_prepare_prints(state); + int r = state_prepare_prints(state, config->compress); if (r != TIZEN_ERROR_NONE) return r; } diff --git a/src/libdlogutil/logretrieve.h b/src/libdlogutil/logretrieve.h index 4e167a8..b28d51c 100644 --- a/src/libdlogutil/logretrieve.h +++ b/src/libdlogutil/logretrieve.h @@ -46,6 +46,6 @@ typedef struct dlogutil_state { log_id_t aliased[LOG_ID_MAX]; } dlogutil_state_s; -int create_initial_fdis(struct fd_info ***fdis, int enabled_buffers, bool is_pipe, const struct log_config *conf, log_id_t aliased[LOG_ID_MAX]); +int create_initial_fdis(struct fd_info ***fdis, int enabled_buffers, bool is_pipe, bool is_compressed_memory, const struct log_config *conf, log_id_t aliased[LOG_ID_MAX]); int dlogutil_state_init(dlogutil_state_s *state, struct fd_info ***data_fds_ptr, int fd_count, dlogutil_config_s *config, bool sorting_needed, dlogutil_sorting_order_e real_sort_by, const struct log_config *conf, log_id_t aliased[LOG_ID_MAX]); int do_print_once(dlogutil_state_s *state, int timeout, dlogutil_entry_s **out); diff --git a/src/logger/dlogutil_line.c b/src/logger/dlogutil_line.c index bdc3134..a582c15 100644 --- a/src/logger/dlogutil_line.c +++ b/src/logger/dlogutil_line.c @@ -12,6 +12,7 @@ bool initialize_dlogutil_line_params(struct dlogutil_line_params *params, struct params->is_dumping = false; params->buf_id = LOG_ID_INVALID; params->file_path = NULL; + params->compression = NULL; params->filter = log_filter_new(); if (!params->filter) @@ -24,6 +25,7 @@ void free_dlogutil_line_params(struct dlogutil_line_params *params) { logfile_free(¶ms->file); log_filter_free(params->filter); + free(params->compression); free(params->file_path); } @@ -76,6 +78,7 @@ static int get_dlogutil_params_from_argc_argv(int argc, char **argv, struct dlog return -ENOMEM; } + params->compression = pr.compression ? strdup(pr.compression) : NULL; params->file.rotate_size_kbytes = pr.rotate_size_kbytes; params->file.max_rotated = pr.max_rotated; params->file.format.format = pr.format; diff --git a/src/logger/dlogutil_line.h b/src/logger/dlogutil_line.h index 462e578..76e84da 100644 --- a/src/logger/dlogutil_line.h +++ b/src/logger/dlogutil_line.h @@ -1,6 +1,7 @@ #pragma once #include +#include /** * @brief Settings for output buffering functionality @@ -16,6 +17,7 @@ struct buf_params { struct dlogutil_line_params { bool monitor; /**< Whether the monitor mode is enabled */ bool is_dumping; /**< Whether the dump mode is enabled */ + char *compression; /**< Compressed memory backup parameter */ struct log_file file; /**< The file information structure */ log_id_t buf_id; /**< The buffer being dumped, or 0 if none selected */ char *file_path; /**< The file to be written to, or NULL if none selected */ diff --git a/src/logger/log_buffer.c b/src/logger/log_buffer.c index e0fd735..443567c 100644 --- a/src/logger/log_buffer.c +++ b/src/logger/log_buffer.c @@ -1,6 +1,7 @@ #include "log_buffer.h" #include "logger_internal.h" #include "subreader_dlogutil.h" +#include "reader_memory.h" #include /** @@ -192,7 +193,7 @@ static int service_writer_stdout(struct logger *server, struct writer *wr, struc /** * @brief Service util request - * @details Handle a request from util + * @details Handle a standard (not compressed memory) request from util * @param[in] server The logger server * @param[in] wr The writer who sent the request * @param[in] msg The message containing the request @@ -231,6 +232,15 @@ static int service_writer_handle_req_util(struct logger *server, struct writer * goto cleanup; } + if (params.compression) { + /* Memory compression is only available from the text + * config. libdlogutil clients have a separate request + * type to obtain data. Eventually it would be good to + * merge them together, but that requires some thought + * put into it. */ + goto cleanup; + } + if (params.file_path) { /* Do not trust writer-based readers (only config-based). * The control socket's privilege checks are fairly lenient @@ -283,6 +293,94 @@ cleanup: return retval; } +static int service_writer_handle_req_compressed_memory_util(struct logger *server, struct writer *wr, struct dlog_control_msg *msg) +{ + assert(server); + assert(wr); + assert(msg); + + assert(msg->request == DLOG_REQ_HANDLE_COMPRESSED_LOGUTIL); + + if (msg->length <= sizeof(struct dlog_control_msg) || + msg->length > sizeof(struct dlog_control_msg) + MAX_LOGGER_REQUEST_LEN) + return -EINVAL; + + if (msg->data[msg->length - sizeof(struct dlog_control_msg)] != 0) + return -EINVAL; + + __attribute__((cleanup(reader_free_ptr))) struct reader_memory *reader = NULL; + + int retval; + __attribute__((cleanup(free_dlogutil_line_params))) struct dlogutil_line_params params; + if (!initialize_dlogutil_line_params(¶ms, (struct buf_params) { })) { + /* TODO: cleanup discards this value, so there isn't much + * point setting it. Ideally it would be attached to the + * reply but that's a protocol change so not worth it atm */ + // retval = -ENOMEM; + goto cleanup; + } + + retval = get_dlogutil_line_params(msg->data, ¶ms); + if (retval < 0) { + // retval = -ENOMEM; // see above + goto cleanup; + } + + if (params.file_path) { + /* Do not trust writer-based readers (only config-based). + * The control socket's privilege checks are fairly lenient + * so this prevents people from asking us to overwrite + * some potentially important files at logger privilege. + * + * At some point it would be good to be able to skip the + * middleman and become able to write to a file directly + * though. The daemon should become able to receive an + * opened file descriptor from a writer. */ + // retval = -EPERM; // see above + goto cleanup; + } + + if (!params.compression) + goto cleanup; + + retval = reader_memory_init_with_writer(&reader, wr, server, params.compression, params.monitor, params.is_dumping); + if (retval != 0) + goto cleanup; + + int pipe_fd[2] = { -1, -1 }; + retval = create_fifo_fds(server, pipe_fd, 0, reader->is_dumping); + if (retval < 0) + goto cleanup; + + retval = reader_add_subreader_dlogutil(&reader->common, params.filter, pipe_fd[1]); + if (retval != 0) + goto cleanup; + + set_write_fd_entity(&reader->common.fd_entity_sink, pipe_fd[1]); + retval = send_pipe(wr->fd_entity.fd, pipe_fd[0]); + if (pipe_fd[0] > 0) + close(pipe_fd[0]); + if (retval) + goto cleanup; + + retval = add_reader_memory(server, reader); + if (retval < 0) + goto cleanup; + + reader = NULL; + return 0; + +cleanup: + /* NB: reply success means that stuff is still stable enough that the client + * can probably get a second chance, so return a success from the whole func + * so as not to drop the client */ + retval = send_dlog_reply(wr->fd_entity.fd, DLOG_REQ_HANDLE_COMPRESSED_LOGUTIL, DLOG_REQ_RESULT_ERR, NULL, 0); + if (retval < 0) + printf("ERROR: both create_reader_from_dlogutil_line() and send_dlog_reply() failed\n"); + + return retval; +} + static int service_writer_handle_req_get_usage(struct logger *server, struct writer *wr, struct dlog_control_msg *msg) { assert(server); @@ -384,6 +482,7 @@ static int service_writer_handle_req_ctrl(struct logger *server, struct writer * switch (msg->request) { case DLOG_REQ_CLEAR: ret = service_writer_handle_req_clear (server, wr, msg); break; case DLOG_REQ_HANDLE_LOGUTIL: ret = service_writer_handle_req_util (server, wr, msg); break; + case DLOG_REQ_HANDLE_COMPRESSED_LOGUTIL: ret = service_writer_handle_req_compressed_memory_util (server, wr, msg); break; case DLOG_REQ_GET_CAPACITY: ret = service_writer_handle_req_get_capacity (server, wr, msg); break; case DLOG_REQ_GET_USAGE: ret = service_writer_handle_req_get_usage (server, wr, msg); break; case DLOG_REQ_GLOBAL_ENABLE_DISABLE_STDOUT: ret = service_writer_handle_req_global_enable_disable_stdout(server, wr, msg); break; diff --git a/src/logger/logger.c b/src/logger/logger.c index 6f2f4ce..233272a 100644 --- a/src/logger/logger.c +++ b/src/logger/logger.c @@ -22,6 +22,7 @@ #include "logger_privileges.h" #include "subreader_file.h" #include "subreader_metrics.h" +#include "subreader_memory.h" #include #include @@ -222,6 +223,25 @@ int add_reader_pipe(struct logger *server, struct reader_pipe *reader) return ret; } +int add_reader_memory(struct logger *server, struct reader_memory *reader) +{ + assert(reader); + assert(server); + + int ret = add_reader_common(server, &reader->common); + if (ret < 0) + return ret; + + // `readers_logger` actually accepts any readers + if (!list_add(&server->readers_logger, reader)) { + if (reader->common.fd_entity_sink.fd >= 0) + remove_fd_entity(&server->epoll_common, &reader->common.fd_entity_sink); + return -ENOMEM; + } + + return 0; +} + static int add_reader_logger(struct logger *server, struct reader_logger *reader) { assert(reader); @@ -276,8 +296,40 @@ int create_fifo_fds(struct logger *server, int pipe_fd[2], int flags, bool dump) return ret; } +static int create_memory_subreader_for_common(struct dlogutil_line_params *params, struct reader_common *reader, struct logger *server) +{ + struct log_compressed_storage *storage = log_compressed_storage_create(params->file.rotate_size_kbytes, params->compression); + if (!storage) + return -ENOMEM; + + list_add(&server->compressed_memories, storage); + + return reader_add_subreader_memory(reader, params->filter, storage); +} + +static int create_memory_subreader_from_dlogutil_line(struct dlogutil_line_params *params, struct logger *server) +{ + assert(params); + assert(params->compression); + + if (params->file_path) // We're writing to memory, not to file + return -EINVAL; + + if (params->buf_id == LOG_ID_INVALID) + return -EINVAL; + + struct reader_logger *const reader = g_backend.logger_readers[params->buf_id]; + if (!reader) + return -ENOENT; + + return create_memory_subreader_for_common(params, &reader->common, server); +} + static int create_logger_subreader_from_dlogutil_line(struct dlogutil_line_params *params) { + assert(params); + assert(!params->compression); + if (params->file_path) { int retval = logfile_set_path(¶ms->file, params->file_path); if (retval < 0) @@ -322,16 +374,27 @@ static int create_reader_pipe_from_dlogutil_line(struct dlogutil_line_params *pa if (params->buf_id == LOG_ID_INVALID) return -EINVAL; - if (params->file.path == NULL) + if ((params->file.path == NULL && !params->compression) + || (params->file.path != NULL && params->compression)) return -EINVAL; retval = reader_pipe_init(&reader, params->buf_id, server, params->monitor, params->is_dumping); if (retval != 0) return retval; - retval = reader_add_subreader_file(&reader->common, params->filter, ¶ms->file, server->buffers[params->buf_id]->sort_by); - if (retval != 0) - return retval; + if (params->file.path == NULL) { + assert(params->compression); + retval = create_memory_subreader_for_common(params, &reader->common, server); + if (retval != 0) + return retval; + } else { + /* FIXME: in theory these could be added independently (1 reader 2 subs), + * but so far that has not been needed and it sounds too fragile to do in haste. */ + assert(!params->compression); + retval = reader_add_subreader_file(&reader->common, params->filter, ¶ms->file, server->buffers[params->buf_id]->sort_by); + if (retval != 0) + return retval; + } *rd = reader; reader = NULL; @@ -1141,7 +1204,10 @@ void parse_logfile_config(void *value, void *userdata) int r; if (g_backend.use_logger_by_default && is_core_buffer(params.buf_id)) { - r = create_logger_subreader_from_dlogutil_line(¶ms); + r = params.compression + ? create_memory_subreader_from_dlogutil_line(¶ms, server) + : create_logger_subreader_from_dlogutil_line(¶ms) + ; } else { struct reader_pipe *reader = NULL; r = create_reader_pipe_from_dlogutil_line(¶ms, server, &reader); diff --git a/src/logger/logger_internal.h b/src/logger/logger_internal.h index b65b149..c264ce1 100644 --- a/src/logger/logger_internal.h +++ b/src/logger/logger_internal.h @@ -31,6 +31,7 @@ #include "fd_entity.h" #include "reader_common.h" #include "reader_logger.h" +#include "reader_memory.h" #include "reader_pipe.h" #include "socket.h" #include "dlogutil_line.h" @@ -136,6 +137,7 @@ struct logger { struct buf_params buf_params; int exiting; struct qos_module qos; + list_head compressed_memories; }; struct logger_config_data { @@ -164,6 +166,7 @@ int service_writer_syslog(struct logger *server, struct writer *wr, struct epoll void logger_add_writer(struct logger *l, struct writer *wr); int create_fifo_fds(struct logger *server, int pipe_fd[2], int flags, bool dump); int add_reader_pipe(struct logger *server, struct reader_pipe *reader); +int add_reader_memory(struct logger *server, struct reader_memory *reader); void flush_logfile_timely(struct log_file *file, struct timespec ts, int flush_time); int get_now(struct now_t *now); diff --git a/src/logger/reader_memory.c b/src/logger/reader_memory.c new file mode 100644 index 0000000..d543c78 --- /dev/null +++ b/src/logger/reader_memory.c @@ -0,0 +1,198 @@ +#include "reader_memory.h" +#include "logger_internal.h" +#include "log_compressed_storage.h" +#include "compression_common.h" +#include "fastlz.h" + +static void reader_memory_free(struct reader_common *_reader) +{ + struct reader_memory *reader = (struct reader_memory *) _reader; + + if (reader->log_compressed_storage_reader_ptr) + log_compressed_storage_release_reader(reader->log_compressed_storage_reader_ptr); + free(reader->uncompressed_chunk); +} + +static int print_out_logs(struct reader_common *_reader, struct now_t _time); + +static void unpin_and_free(struct reader_memory *reader, struct logger *server) +{ + list_remove(&server->readers_logger, reader); + remove_reader_fd_entities(server, &reader->common); + reader_free(&reader->common); +} + +static void dispatch_event_reader_memory(struct logger *server, struct epoll_event *event, void *userdata) +{ + struct reader_memory *const rm = (struct reader_memory *) userdata; + assert(rm); + + if (event->events & (EPOLLHUP | EPOLLERR)) { + unpin_and_free(rm, server); + return; + } + + struct now_t now; + int r = get_now(&now); + if (r < 0) { + unpin_and_free(rm, server); + return ; + } + + r = rm->common.service_reader(&rm->common, now); + if (r != 0) { + unpin_and_free(rm, server); + return; + } +} + +static struct reader_memory *reader_memory_alloc(bool monitor, bool is_dumping) +{ + struct reader_memory *ret = calloc(1, sizeof(*ret)); + if (!ret) + return NULL; + + init_fd_entity(&ret->common.fd_entity_sink , NULL, NULL); + init_fd_entity(&ret->common.fd_entity_source, NULL, NULL); + ret->common.free_reader = reader_memory_free; + + ret->uncompressed_chunk = malloc(COMPRESSION_CHUNK_SIZE); + if (!ret->uncompressed_chunk) { + free(ret); + return NULL; + } + ret->uncompressed_chunk_size = 0; + ret->uncompressed_chunk_index = 0; + + ret->common.service_reader = print_out_logs; + ret->monitor = monitor; + ret->is_dumping = is_dumping; + ret->log_compressed_storage_reader_ptr = NULL; + + return ret; +} + +bool find_str(void *element, void *userdata) +{ + return !strcmp(log_compressed_storage_get_name((log_compressed_storage *) element), (char *) userdata); +} + +void ignore_dropped_log(const struct compression_entry *ce, void *user_data) +{ + /* A compressed chunk is typically too large to try and salvage it. + * Drop it, it's quite unlikely that this actually happens given + * that a client should process logs quite fast compared to how + * slow they are produced. */ + + fprintf(stderr, "reader_memory: dropped a compressed chunk of length %zu / %zu\n" + , ce->size_out + , ce->size_in + ); +} + +int reader_memory_init_with_writer(struct reader_memory **reader, struct writer *writer, struct logger *server, + char *compress, bool monitor, bool is_dumping) +{ + assert(reader); + assert(writer); + assert(writer->buf_ptr); + assert(server); + assert(compress); + + log_compressed_storage *storage = list_find_if(server->compressed_memories, compress, find_str); + if (!storage) + return -EINVAL; + + __attribute__((cleanup(reader_free_ptr))) struct reader_memory *ret = reader_memory_alloc(monitor, is_dumping); + if (!ret) + return -ENOMEM; + + ret->log_compressed_storage_reader_ptr = log_compressed_storage_new_reader(storage, + is_dumping, monitor, ignore_dropped_log, ret); + if (!ret->log_compressed_storage_reader_ptr) + return -ENOMEM; + + init_fd_entity(&ret->common.fd_entity_sink, dispatch_event_reader_memory, ret); + init_fd_entity(&ret->common.fd_entity_source, dispatch_event_reader_memory, ret); + + *reader = ret; + ret = NULL; + return 0; +} + +int spend_queued_chunk(struct reader_memory *reader) +{ + while (reader->uncompressed_chunk_index < reader->uncompressed_chunk_size) { + dlogutil_entry_s *const due = (dlogutil_entry_s *)(reader->uncompressed_chunk + reader->uncompressed_chunk_index); + if (!due->len) + return EINVAL; + + reader->uncompressed_chunk_index += due->len; + + int r = list_foreach_ret(reader->common.subs, due, subreader_apply_log); + if (r != 0) + return r; + } + + return 0; +} + +void reader_memory_decompress_chunk(struct reader_memory *reader, const struct compression_entry *ce) +{ + if (reader->uncompressed_chunk_index < reader->uncompressed_chunk_size) + return; + + /* Could also allocate the chunk here instead of a "static" large one, + * which saves some memory overhead in the optimistic case, but + * adds a lot of juggling around instead. */ + + reader->uncompressed_chunk_index = 0; + reader->uncompressed_chunk_size = ce->size_in; + + if (!ce->size_out){ + // Data wasn't compressed + memcpy(reader->uncompressed_chunk, ce->compressed_data, ce->size_in); + } else { + int decompressed_size = fastlz_decompress(ce->compressed_data, ce->size_out, reader->uncompressed_chunk, ce->size_in); + assert((size_t) decompressed_size == ce->size_in); + } +} + +/** + * @brief Print out logs + * @details Make sure the reader is up to date on printed logs + * @param[in] reader The reader to read the data + * @param[in] _time Unused timestamps + * @return 0 if data remains for the next iteration, 1 if the buffer is to be removed, else -1 + */ +static int print_out_logs(struct reader_common *_reader, struct now_t _time) +{ + struct reader_memory *const reader = (struct reader_memory *) _reader; + assert(reader); + + int r = reader_flush(_reader, (struct timespec){0, 0}, 0); + if (r != 0) + return r > 0; + + r = spend_queued_chunk(reader); + if (r != 0) + return r > 0; + + while (log_compressed_storage_reader_is_new_entry_available(reader->log_compressed_storage_reader_ptr)) { + const struct compression_entry *ce = log_compressed_storage_reader_get_new_entry(reader->log_compressed_storage_reader_ptr); + assert(ce); // `is_new_entry_available` guarantees this + + reader_memory_decompress_chunk(reader, ce); + + r = spend_queued_chunk(reader); + if (r != 0) + return r > 0; + } + + /* Treat all readers as dumping. This is because monitoring makes + * little sense here (monitor the source instead!) and because + * these readers have quite a lot of overhead (so it's best if + * they don't linger around too long, even if by accident). */ + return 1; +} + diff --git a/src/logger/reader_memory.h b/src/logger/reader_memory.h new file mode 100644 index 0000000..55d3319 --- /dev/null +++ b/src/logger/reader_memory.h @@ -0,0 +1,23 @@ +#pragma once + +#include "reader_common.h" + +#include +#include + +struct log_compressed_storage_reader; +struct writer; +struct logger; + +struct reader_memory { + struct reader_common common; + struct log_compressed_storage_reader *log_compressed_storage_reader_ptr; + bool is_dumping; + bool monitor; + char *uncompressed_chunk; + size_t uncompressed_chunk_size; + size_t uncompressed_chunk_index; +}; + +int reader_memory_init_with_writer(struct reader_memory **reader, struct writer *writer, struct logger *server, + char *compress, bool monitor, bool is_dumping); diff --git a/src/logger/subreader_memory.c b/src/logger/subreader_memory.c new file mode 100644 index 0000000..43f5dec --- /dev/null +++ b/src/logger/subreader_memory.c @@ -0,0 +1,108 @@ +#include "queued_entry.h" +#include "subreader_memory.h" +#include "compression_common.h" + +/* Don't flush, even on an explicit flush request, if + * the buffer is not filled at least this much. + * + * Should be lower than the full chunk size by at least + * a full log entry including metadata (about 4200). + * + * Keep in mind flushes occur periodically, every 99 logs + * and/or 600ms by default, so while it would be good to + * respect flush requests they happen a bit too frequently + * to do that at the moment (since compressing single logs + * gives fairly underwhelming results). */ +#define NO_FLUSH_SIZE (COMPRESSION_CHUNK_SIZE - 10000) + +static void subreader_memory_free(void *userdata) +{ + struct subreader_memory *const srm = (struct subreader_memory *) userdata; + assert(srm); + + free(srm->to_be_compressed); +} + +static int subreader_memory_flush(void *userdata, struct timespec ts, int flush_time) +{ + struct subreader_memory *const srm = (struct subreader_memory *) userdata; + assert(srm); + + /* Compression minimum buffer requirement. + * Keep this even if excessive flushing is + * fixed and the check below is removed. */ + if (srm->to_be_compressed_size < 16) + return 0; + + if (srm->to_be_compressed_size < NO_FLUSH_SIZE) + return 0; + + /* Disregard failures (other than a mention), since + * we can't do anything about insufficient memory. */ + if (!log_compressed_storage_add_new_entry(srm->storage, srm->to_be_compressed, srm->to_be_compressed_size)) { + fprintf(stderr, "subreader_memory: failed to compress a chunk of size %zu\n" + , srm->to_be_compressed_size + ); + } + srm->to_be_compressed_size = 0; + + return 0; +} + +static int subreader_memory_apply_log(const struct subreader_common *sub, const struct dlogutil_entry *due) +{ + assert(sub); + assert(due); + + struct subreader_memory *const srm = (struct subreader_memory *) sub->sub_userdata; + assert(srm); + + if (due->len + srm->to_be_compressed_size > COMPRESSION_CHUNK_SIZE) { + subreader_memory_flush(srm, (struct timespec){0, 0}, 0); + if (due->len + srm->to_be_compressed_size > COMPRESSION_CHUNK_SIZE) + return -1; + } + + memcpy(srm->to_be_compressed + srm->to_be_compressed_size, due, due->len); + srm->to_be_compressed_size += due->len; + + return 0; +} + +int reader_add_subreader_memory(struct reader_common *reader, struct log_filter *filter, struct log_compressed_storage *storage) +{ + assert(reader); + assert(filter); + assert(storage); + + struct subreader_common *const sub = malloc(sizeof *sub); + struct subreader_memory *const srm = malloc(sizeof *srm); + if (!sub || !srm) + goto free_structs; + + srm->storage = storage; + srm->to_be_compressed_size = 0; + srm->to_be_compressed = malloc(COMPRESSION_CHUNK_SIZE); // FIXME: Maybe should just be `char to_be_compressed[COMPRESSION_CHUNK_SIZE]` instead? Looks too huge though + if (!srm->to_be_compressed) + goto free_srm_innards; + + sub->sub_userdata = srm; + sub->sub_destroy = subreader_memory_free; + sub->sub_apply_log = subreader_memory_apply_log; + sub->sub_flush = subreader_memory_flush; + sub->filter = log_filter_move(filter); + + list_add(&reader->subs, sub); + + return 0; + +free_srm_innards: + free(srm->to_be_compressed); + +free_structs: + free(srm); + free(sub); + + return -ENOMEM; +} + diff --git a/src/logger/subreader_memory.h b/src/logger/subreader_memory.h new file mode 100644 index 0000000..0a1f168 --- /dev/null +++ b/src/logger/subreader_memory.h @@ -0,0 +1,13 @@ +#pragma once + +#include "reader_common.h" +#include "log_compressed_storage.h" + +struct subreader_memory { + struct log_compressed_storage *storage; + char *to_be_compressed; + size_t to_be_compressed_size; +}; + +int reader_add_subreader_memory(struct reader_common *reader, struct log_filter *filter, struct log_compressed_storage *storage); + diff --git a/src/logutil/logutil.c b/src/logutil/logutil.c index 70c3d97..41f2f05 100644 --- a/src/logutil/logutil.c +++ b/src/logutil/logutil.c @@ -137,7 +137,7 @@ static int for_each_buffer(int enabled_buffers, int (*func)(dlogutil_state_s *st * @retval <0 Failure as denoted by value: -errno */ static int do_print(dlogutil_mode_e mode, unsigned int dump_size, int enabled_buffers, dlogutil_sorting_order_e sort_by, - dlogutil_config_s *config, struct log_file *l_file) + dlogutil_config_s *config, struct log_file *l_file, const char *compress) { /* Optimisation for short-lived (i.e. dumping) instances. * @@ -206,12 +206,13 @@ static int do_print(dlogutil_mode_e mode, unsigned int dump_size, int enabled_bu dlogutil_config_buffer_add(config, (log_id_t) i); dlogutil_state_s *state; - if (mode == DLOGUTIL_MODE_MONITOR) - r = dlogutil_config_mode_set_monitor(config); - else if (mode == DLOGUTIL_MODE_CONTINUOUS) - r = dlogutil_config_mode_set_continuous(config); - else - r = dlogutil_config_mode_set_dump(config, dump_size); + switch (mode) { + case DLOGUTIL_MODE_MONITOR: r = dlogutil_config_mode_set_monitor (config ); break; + case DLOGUTIL_MODE_CONTINUOUS: r = dlogutil_config_mode_set_continuous (config ); break; + case DLOGUTIL_MODE_DUMP: r = dlogutil_config_mode_set_dump (config, dump_size); break; + case DLOGUTIL_MODE_COMPRESSED_MEMORY_DUMP: r = dlogutil_config_mode_set_compressed_memory_dump(config, compress ); break; + default: assert(false); + } if (!r) r = dlogutil_config_connect(config, &state); @@ -369,7 +370,7 @@ int main(int argc, char **argv) int r; switch (pr.action) { case ACTION_PRINT: { - r = do_print(pr.mode, pr.dump_size, enabled_buffers, pr.sort_by, config, &l_file); + r = do_print(pr.mode, pr.dump_size, enabled_buffers, pr.sort_by, config, &l_file, pr.compression); break; } case ACTION_GET_CAPACITY: { diff --git a/src/shared/util_parser.c b/src/shared/util_parser.c index 5994d0f..b34c8f5 100644 --- a/src/shared/util_parser.c +++ b/src/shared/util_parser.c @@ -21,6 +21,7 @@ struct parse_result parse_options(int argc, char **argv) size_t rotate_size_kbytes = DEFAULT_ROTATE_SIZE_KB; size_t max_rotated = DEFAULT_ROTATE_NUM_FILES; const char *file_path = NULL; + char *compression = NULL; size_t write_buffer_size = 0; int enabled_buffers = 0; action_e action = ACTION_PRINT; @@ -40,12 +41,13 @@ struct parse_result parse_options(int argc, char **argv) while (1) { static const struct option long_options[] = { - {"tid" , required_argument, NULL, 0}, - {"pid" , required_argument, NULL, 1}, - {"version", no_argument, NULL, 2}, - {"color" , required_argument, NULL, 3}, - {"sort-by", required_argument, NULL, 4}, - {"help" , no_argument, NULL, 'h'}, + {"tid" , required_argument, NULL, 0}, + {"pid" , required_argument, NULL, 1}, + {"version" , no_argument, NULL, 2}, + {"color" , required_argument, NULL, 3}, + {"sort-by" , required_argument, NULL, 4}, + {"compress", required_argument, NULL, 5}, + {"help" , no_argument, NULL, 'h'}, {0} }; int option = getopt_long(argc, argv, "cdmt:gsf:r:n:v:b:u:e:h", long_options, NULL); @@ -109,6 +111,10 @@ struct parse_result parse_options(int argc, char **argv) return (struct parse_result) { .status = PARSE_BAD_SORT_BY, .bad_contents = optarg, }; } break; + case 5: /* memory compression */ + mode = DLOGUTIL_MODE_COMPRESSED_MEMORY_DUMP; + compression = optarg; + break; case 'd': mode = DLOGUTIL_MODE_DUMP; break; @@ -208,6 +214,7 @@ struct parse_result parse_options(int argc, char **argv) .mode = mode, .dump_size = dump_size, .sort_by = sort_by, + .compression = compression, }; filterspecs = NULL; pid_filters = NULL; diff --git a/src/tests/fdi_logger_neg.c b/src/tests/fdi_logger_neg.c index 36a684a..fb5fddb 100644 --- a/src/tests/fdi_logger_neg.c +++ b/src/tests/fdi_logger_neg.c @@ -70,10 +70,10 @@ int main(void) expected_ioctl = LOGGER_GET_LOG_LEN; ioctl_ret = -67; - assert(-67 == ops_logger.prepare_print(&fdi, 123, false, (struct log_filter *) 0xCA5CADE)); + assert(-67 == ops_logger.prepare_print(&fdi, 123, false, (struct log_filter *) 0xCA5CADE, NULL)); expected_ioctl = -1; - assert(!ops_logger.prepare_print(&fdi, 0, false, (struct log_filter *) 0xCA5CADE)); + assert(!ops_logger.prepare_print(&fdi, 0, false, (struct log_filter *) 0xCA5CADE, NULL)); fail_read = true; assert(-334 == ops_logger.read(&fdi)); diff --git a/src/tests/fdi_logger_pos.c b/src/tests/fdi_logger_pos.c index 815c899..6f44fa8 100644 --- a/src/tests/fdi_logger_pos.c +++ b/src/tests/fdi_logger_pos.c @@ -79,7 +79,7 @@ int main(void) expected_ioctl = LOGGER_GET_LOG_LEN; ioctl_ret = 0; - assert(1 == ops_logger.prepare_print(&fdi, 123, false, (struct log_filter *) 0xCA5CADE)); + assert(1 == ops_logger.prepare_print(&fdi, 123, false, (struct log_filter *) 0xCA5CADE, NULL)); monitor_more_reads(3, &fdi); monitor_more_reads(6, &fdi); diff --git a/src/tests/fdi_pipe_neg.c b/src/tests/fdi_pipe_neg.c index a1c6d9f..6cca43b 100644 --- a/src/tests/fdi_pipe_neg.c +++ b/src/tests/fdi_pipe_neg.c @@ -50,26 +50,31 @@ int main(void) correct_send_data = "dlogutil filter0:V *:S"; correct_send_datalen = strlen(correct_send_data) + 1; send_return = -1; - assert(ops_pipe.prepare_print(&info, false, false, filter) == -1); + assert(ops_pipe.prepare_print(&info, false, false, filter, NULL) == -1); correct_send_data = "dlogutil -d filter0:V *:S"; correct_send_datalen = strlen(correct_send_data) + 1; - assert(ops_pipe.prepare_print(&info, true, false, filter) == -1); + assert(ops_pipe.prepare_print(&info, true, false, filter, NULL) == -1); log_filter_set_filterspec(filter, "filter1"); correct_send_data = "dlogutil filter1:V filter0:V *:S"; correct_send_datalen = strlen(correct_send_data) + 1; - assert(ops_pipe.prepare_print(&info, false, false, filter) == -1); + assert(ops_pipe.prepare_print(&info, false, false, filter, NULL) == -1); log_filter_set_tid(filter, 123); log_filter_set_pid(filter, 456); correct_send_data = "dlogutil --pid 456 --tid 123 filter1:V filter0:V *:S"; correct_send_datalen = strlen(correct_send_data) + 1; - assert(ops_pipe.prepare_print(&info, false, false, filter) == -1); + assert(ops_pipe.prepare_print(&info, false, false, filter, NULL) == -1); + + correct_request = DLOG_REQ_HANDLE_COMPRESSED_LOGUTIL; + correct_send_data = "dlogutil --compress membackup --pid 456 --tid 123 filter1:V filter0:V *:S"; + correct_send_datalen = strlen(correct_send_data) + 1; + assert(ops_pipe.prepare_print(&info, false, false, filter, (char *) "membackup") == -1); send_return = 0; recv_pipe_fail = true; - assert(ops_pipe.prepare_print(&info, false, false, filter) == -EIO); + assert(ops_pipe.prepare_print(&info, false, false, filter, (char *) "membackup") == -EIO); recv_pipe_fail = false; // Too long filter list test @@ -81,7 +86,7 @@ int main(void) /* This is a long filter. In fact, it's too long to be accepted * by ops_pipe.prepare_print, which should return -E2BIG. */ log_filter_set_filterspec(filter, too_long_filter); - assert(ops_pipe.prepare_print(&info, false, false, filter) == -E2BIG); + assert(ops_pipe.prepare_print(&info, false, false, filter, (char *) "membackup") == -E2BIG); log_filter_free(filter); correct_sockfd = 0; diff --git a/src/tests/fdi_pipe_pos.c b/src/tests/fdi_pipe_pos.c index 44e1d56..a90d580 100644 --- a/src/tests/fdi_pipe_pos.c +++ b/src/tests/fdi_pipe_pos.c @@ -74,7 +74,7 @@ void big_filter_test() // NB: both strings end in a space; the input filter should be parsed correctly despite this strncat(very_big_string_for_comparison, "*:S", sizeof(very_big_string_for_comparison) - 1); - correct_sockfd = 3; + correct_sockfd = 4; correct_request = DLOG_REQ_HANDLE_LOGUTIL; send_return = 0; @@ -84,7 +84,7 @@ void big_filter_test() correct_send_datalen = strlen(very_big_string_for_comparison) + 1; /* long data test */ log_filter_set_filterspec(filter, just_big_enough_filter); - assert(ops_pipe.prepare_print(&info, false, false, filter) == 0); + assert(ops_pipe.prepare_print(&info, false, false, filter, NULL) == 0); correct_request = DLOG_REQ_CLEAR; correct_send_data = NULL; @@ -144,11 +144,17 @@ int main(void) recv_pipe_fail = false; correct_send_data = "dlogutil --pid 456 --tid 123 filter1:V filter0:V *:S"; correct_send_datalen = strlen(correct_send_data) + 1; - assert(ops_pipe.prepare_print(&info, false, false, filter) == 0); + assert(ops_pipe.prepare_print(&info, false, false, filter, NULL) == 0); assert(info.fd == 2); + correct_request = DLOG_REQ_HANDLE_COMPRESSED_LOGUTIL; + correct_send_data = "dlogutil --compress membackup --pid 456 --tid 123 filter1:V filter0:V *:S"; + correct_send_datalen = strlen(correct_send_data) + 1; + assert(ops_pipe.prepare_print(&info, false, false, filter, (char *) "membackup") == 0); + assert(info.fd == 3); + log_filter_free(filter); - correct_sockfd = 2; + correct_sockfd = 3; read_errno = 0; read_datalen = sizeof(dlogutil_entry_s) + 4; @@ -210,7 +216,6 @@ ok: assert(ops_pipe.has_log(&info)); assert(!ops_pipe.extract_entry(&info)); __real_free(read_data); - correct_sockfd = 1; correct_request = DLOG_REQ_CLEAR; correct_send_data = NULL;