3 * Copyright (c) 2020 Samsung Electronics Co., Ltd
5 * Permission is hereby granted, free of charge, to any person obtaining a copy
6 * of this software and associated documentation files (the "Software"), to deal
7 * in the Software without restriction, including without limitation the rights
8 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 * copies of the Software, and to permit persons to whom the Software is furnished
10 * to do so, subject to the following conditions:
12 * The above copyright notice and this permission notice shall be included in all
13 * copies or substantial portions of the Software.
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
27 #include "deduplicate.h"
29 #include "logcommon.h"
32 #define WARNING_STRING " LOG DUPLICATED %d TIMES"
33 #define WARNING_MAX sizeof(" LOG DUPLICATED " STRINGIFY(INT32_MAX) " TIMES")
35 typedef struct deduplicate_log {
40 dlog_deduplicate_e (*deduplicate_func)(const char *, size_t, struct timespec *);
42 static long last_millisec;
43 static long interval_millisec;
44 static int known_hashes_capacity;
45 static deduplicate_log *known_hashes_vector;
46 static int known_hashes_size = 0;
47 static pthread_mutex_t deduplication_lock = PTHREAD_MUTEX_INITIALIZER;
48 static int warn_quantity;
49 static int warning_size;
50 static char warning_content[WARNING_MAX];
51 static deduplicate_log basic_log;
53 static void refresh_deduplication_limits()
55 known_hashes_size = 0;
58 static size_t *known_hash_count(const uint32_t new_hash)
60 for (size_t i = 0; i < known_hashes_size; i++)
61 if (known_hashes_vector[i].hash == new_hash)
62 return &known_hashes_vector[i].quantity;
67 static bool compare_milli(struct timespec *tp)
69 long current_millisec = 1000 * tp->tv_sec + (tp->tv_nsec / 1000000);
71 if ((current_millisec >= last_millisec) && (current_millisec < (last_millisec + interval_millisec)))
74 last_millisec = current_millisec;
78 static dlog_deduplicate_e basic_deduplicate(const char *msg, size_t len, struct timespec *tp)
80 const uint32_t new_hash = create_hash(msg, len);
81 bool compare_hash = (basic_log.hash == new_hash);
82 bool compare_milisec = compare_milli(tp);
84 if (compare_hash && compare_milisec) {
86 if (warn_quantity != 0 && (basic_log.quantity % warn_quantity == 0))
87 return DLOG_DO_NOT_DEDUPLICATE_BUT_WARN;
88 return DLOG_DEDUPLICATE;
90 basic_log.quantity = 1;
91 basic_log.hash = new_hash;
94 return DLOG_DO_NOT_DEDUPLICATE;
97 static bool add_known_hash(uint32_t new_hash)
99 if (known_hashes_size >= known_hashes_capacity) {
100 assert(known_hashes_size < 2 * known_hashes_capacity);
101 typeof(known_hashes_vector) temp = realloc(known_hashes_vector, (2 * known_hashes_capacity * sizeof *temp));
104 known_hashes_capacity *= 2;
105 known_hashes_vector = temp;
107 known_hashes_vector[known_hashes_size].hash = new_hash;
108 known_hashes_vector[known_hashes_size].quantity = 1;
114 static dlog_deduplicate_e advanced_deduplicate(const char *msg, size_t len, struct timespec *tp)
116 pthread_mutex_lock(&deduplication_lock);
117 bool compare_milisec = compare_milli(tp);
119 uint32_t new_hash = create_hash(msg, len);
120 size_t *quantity_ptr = NULL;
121 dlog_deduplicate_e output = DLOG_DO_NOT_DEDUPLICATE;
123 if (compare_milisec) {
124 quantity_ptr = known_hash_count(new_hash);
125 if (quantity_ptr != NULL) {
127 output = DLOG_DEDUPLICATE;
128 if (warn_quantity != 0 && (*quantity_ptr % warn_quantity == 0))
129 output = DLOG_DO_NOT_DEDUPLICATE_BUT_WARN;
132 refresh_deduplication_limits();
135 if (quantity_ptr == NULL)
136 add_known_hash(new_hash);
137 pthread_mutex_unlock(&deduplication_lock);
142 void deduplicate_warn(char *buf, size_t size, size_t len)
144 assert(size > warning_size);
146 if (len + warning_size < size)
147 strncpy(buf + len, warning_content, size - len);
149 strncpy(buf + size - warning_size - 1, warning_content, warning_size + 1);
152 void __configure_deduplicate(struct log_config *config)
155 const char *const value = log_config_get(config, "deduplicate_method");
156 interval_millisec = log_config_get_int(config, "deduplicate_interval_ms", 1);
158 const char *warn_quant_str = log_config_get(config, "deduplicate_warn_quantity");
161 else if (parse_number(&warn_quant_str, &warn_quantity) < 0 || (warn_quantity < 0))
164 warning_size = snprintf(warning_content, sizeof warning_content, WARNING_STRING, warn_quantity);
165 assert(warning_size < (sizeof warning_content));
166 if (warning_size < 0)
171 } else if (strcmp(value, "only_identical_neighbours") == 0) {
172 deduplicate_func = basic_deduplicate;
173 } else if (strcmp(value, "all_identical_logs") == 0) {
174 known_hashes_capacity = 4;
175 known_hashes_vector = (deduplicate_log *) calloc(known_hashes_capacity, sizeof *known_hashes_vector);
176 if (!known_hashes_vector)
178 deduplicate_func = advanced_deduplicate;
182 void __deduplicate_destroy()
184 known_hashes_size = 0;
185 free(known_hashes_vector);
186 known_hashes_vector = NULL;