5feda7f6b40aaf5e200b7a5c666e7207ca276f98
[platform/core/system/dlog.git] / src / libdlog / deduplicate.c
1 /*  MIT License
2  *
3  * Copyright (c) 2020 Samsung Electronics Co., Ltd
4  *
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:
11  *
12  * The above copyright notice and this permission notice shall be included in all
13  * copies or substantial portions of the Software.
14  *
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
21  * THE SOFTWARE. */
22
23 // POSIX
24 #include <pthread.h>
25
26 // Dlog
27 #include "deduplicate.h"
28 #include "hash.h"
29 #include "logcommon.h"
30 #include "parsers.h"
31
32 #define WARNING_STRING " LOG DUPLICATED %d TIMES"
33 #define WARNING_MAX sizeof(" LOG DUPLICATED " STRINGIFY(INT32_MAX) " TIMES")
34
35 typedef struct deduplicate_log {
36         uint32_t hash;
37         size_t quantity;
38 } deduplicate_log;
39
40 dlog_deduplicate_e (*deduplicate_func)(const char *, size_t, struct timespec *);
41
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;
52
53 static void refresh_deduplication_limits()
54 {
55         known_hashes_size = 0;
56 }
57
58 static size_t *known_hash_count(const uint32_t new_hash)
59 {
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;
63
64         return NULL;
65 }
66
67 static bool compare_milli(struct timespec *tp)
68 {
69         long current_millisec = 1000 * tp->tv_sec + (tp->tv_nsec / 1000000);
70
71         if ((current_millisec >= last_millisec) && (current_millisec < (last_millisec + interval_millisec)))
72                 return true;
73
74         last_millisec = current_millisec;
75         return false;
76 }
77
78 static dlog_deduplicate_e basic_deduplicate(const char *msg, size_t len, struct timespec *tp)
79 {
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);
83
84         if (compare_hash && compare_milisec) {
85                 basic_log.quantity++;
86                 if (warn_quantity != 0 && (basic_log.quantity % warn_quantity == 0))
87                         return DLOG_DO_NOT_DEDUPLICATE_BUT_WARN;
88                 return DLOG_DEDUPLICATE;
89         } else {
90                 basic_log.quantity = 1;
91                 basic_log.hash = new_hash;
92         }
93
94         return DLOG_DO_NOT_DEDUPLICATE;
95 }
96
97 static bool add_known_hash(uint32_t new_hash)
98 {
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));
102                 if (!temp)
103                         return false;
104                 known_hashes_capacity *= 2;
105                 known_hashes_vector = temp;
106         }
107         known_hashes_vector[known_hashes_size].hash = new_hash;
108         known_hashes_vector[known_hashes_size].quantity = 1;
109         known_hashes_size++;
110
111         return true;
112 }
113
114 static dlog_deduplicate_e advanced_deduplicate(const char *msg, size_t len, struct timespec *tp)
115 {
116         pthread_mutex_lock(&deduplication_lock);
117         bool compare_milisec = compare_milli(tp);
118
119         uint32_t new_hash = create_hash(msg, len);
120         size_t *quantity_ptr = NULL;
121         dlog_deduplicate_e output = DLOG_DO_NOT_DEDUPLICATE;
122
123         if (compare_milisec) {
124                 quantity_ptr = known_hash_count(new_hash);
125                 if (quantity_ptr != NULL) {
126                         *quantity_ptr += 1;
127                         output = DLOG_DEDUPLICATE;
128                         if (warn_quantity != 0 && (*quantity_ptr % warn_quantity == 0))
129                                 output = DLOG_DO_NOT_DEDUPLICATE_BUT_WARN;
130                 }
131         } else {
132                 refresh_deduplication_limits();
133         }
134
135         if (quantity_ptr == NULL)
136                 add_known_hash(new_hash);
137         pthread_mutex_unlock(&deduplication_lock);
138
139         return output;
140 }
141
142 void deduplicate_warn(char *buf, size_t size, size_t len)
143 {
144         assert(size > warning_size);
145
146         if (len + warning_size < size)
147                 strncpy(buf + len, warning_content, size - len);
148         else
149                 strncpy(buf + size - warning_size - 1, warning_content, warning_size + 1);
150 }
151
152 void __configure_deduplicate(struct log_config *config)
153 {
154         assert(config);
155         const char *const value = log_config_get(config, "deduplicate_method");
156         interval_millisec = log_config_get_int(config, "deduplicate_interval_ms", 1);
157
158         const char *warn_quant_str = log_config_get(config, "deduplicate_warn_quantity");
159         if (!warn_quant_str)
160                 warn_quantity = 10;
161         else if (parse_number(&warn_quant_str, &warn_quantity) < 0 || (warn_quantity < 0))
162                 warn_quantity = 0;
163
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)
167                 return;
168
169         if (!value) {
170                 return;
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)
177                         return;
178                 deduplicate_func = advanced_deduplicate;
179         }
180 }
181
182 void __deduplicate_destroy()
183 {
184         known_hashes_size = 0;
185         free(known_hashes_vector);
186         known_hashes_vector = NULL;
187 }