1 // Copyright 2020 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14
15 #include "pw_log_rpc/log_filter.h"
16
17 #include <array>
18 #include <cstddef>
19 #include <cstring>
20
21 #include "pw_bytes/endian.h"
22 #include "pw_log/levels.h"
23 #include "pw_log/log.h"
24 #include "pw_log/proto/log.pwpb.h"
25 #include "pw_log/proto_utils.h"
26 #include "pw_log_rpc/log_filter_map.h"
27 #include "pw_log_tokenized/metadata.h"
28 #include "pw_result/result.h"
29 #include "pw_status/status.h"
30 #include "pw_status/try.h"
31 #include "pw_unit_test/framework.h"
32
33 namespace pw::log_rpc {
34 namespace {
35
36 namespace FilterRule = ::pw::log::pwpb::FilterRule;
37
38 constexpr uint32_t kSampleModule = 0x1234;
39 constexpr uint32_t kSampleFlags = 0x3;
40 const std::array<std::byte, cfg::kMaxThreadNameBytes - 7> kSampleThread = {
41 std::byte('R'), std::byte('P'), std::byte('C')};
42 constexpr char kSampleMessage[] = "message";
43 constexpr auto kSampleModuleLittleEndian =
44 bytes::CopyInOrder<uint32_t>(endian::little, kSampleModule);
45
46 // Creates and encodes a log entry in the provided buffer.
47 template <uintptr_t log_level, uintptr_t module, uintptr_t flags>
EncodeLogEntry(std::string_view message,ByteSpan buffer,ConstByteSpan thread)48 Result<ConstByteSpan> EncodeLogEntry(std::string_view message,
49 ByteSpan buffer,
50 ConstByteSpan thread) {
51 auto metadata = log_tokenized::Metadata::Set<log_level, module, flags, 0>();
52 return log::EncodeTokenizedLog(metadata,
53 as_bytes(span<const char>(message)),
54 /*ticks_since_epoch=*/0,
55 thread,
56 buffer);
57 }
58
EncodeFilterRule(const Filter::Rule & rule,FilterRule::StreamEncoder & encoder)59 Status EncodeFilterRule(const Filter::Rule& rule,
60 FilterRule::StreamEncoder& encoder) {
61 PW_TRY(
62 encoder.WriteLevelGreaterThanOrEqual(rule.level_greater_than_or_equal));
63 PW_TRY(encoder.WriteModuleEquals(rule.module_equals));
64 PW_TRY(encoder.WriteAnyFlagsSet(rule.any_flags_set));
65 PW_TRY(encoder.WriteThreadEquals(rule.thread_equals));
66 return encoder.WriteAction(static_cast<FilterRule::Action>(rule.action));
67 }
68
EncodeFilter(const Filter & filter,ByteSpan buffer)69 Result<ConstByteSpan> EncodeFilter(const Filter& filter, ByteSpan buffer) {
70 log::pwpb::Filter::MemoryEncoder encoder(buffer);
71 for (auto& rule : filter.rules()) {
72 FilterRule::StreamEncoder rule_encoder = encoder.GetRuleEncoder();
73 PW_TRY(EncodeFilterRule(rule, rule_encoder));
74 }
75 return ConstByteSpan(encoder);
76 }
77
VerifyRule(const Filter::Rule & rule,const Filter::Rule & expected_rule)78 void VerifyRule(const Filter::Rule& rule, const Filter::Rule& expected_rule) {
79 EXPECT_EQ(rule.level_greater_than_or_equal,
80 expected_rule.level_greater_than_or_equal);
81 EXPECT_EQ(rule.module_equals, expected_rule.module_equals);
82 EXPECT_EQ(rule.any_flags_set, expected_rule.any_flags_set);
83 EXPECT_EQ(rule.thread_equals, expected_rule.thread_equals);
84 EXPECT_EQ(rule.action, expected_rule.action);
85 }
86
TEST(FilterMap,RetrieveFiltersById)87 TEST(FilterMap, RetrieveFiltersById) {
88 const std::array<std::byte, cfg::kMaxFilterIdBytes> filter_id1{
89 std::byte(0xfe), std::byte(0xed), std::byte(0xba), std::byte(0xb1)};
90 const std::array<std::byte, cfg::kMaxFilterIdBytes> filter_id2{
91 std::byte(0xca), std::byte(0xfe), std::byte(0xc0), std::byte(0xc0)};
92 const std::array<std::byte, cfg::kMaxFilterIdBytes> filter_id3{
93 std::byte(0xfa), std::byte(0xfe), std::byte(0xf1), std::byte(0xf0)};
94 std::array<Filter, 3> filters = {
95 Filter(filter_id1, {}),
96 Filter(filter_id2, {}),
97 Filter(filter_id3, {}),
98 };
99
100 FilterMap filter_map(filters);
101
102 // Check that each filters() element points to the same object provided.
103 span<const Filter> filter_list = filter_map.filters();
104 ASSERT_EQ(filter_list.size(), filters.size());
105 size_t i = 0;
106 for (auto& filter : filter_list) {
107 EXPECT_EQ(&filter, &filters[i++]);
108 }
109
110 auto filter_result = filter_map.GetFilterFromId(filter_id3);
111 ASSERT_TRUE(filter_result.ok());
112 EXPECT_EQ(filter_result.value(), &filters[2]);
113
114 filter_result = filter_map.GetFilterFromId(filter_id2);
115 ASSERT_TRUE(filter_result.ok());
116 EXPECT_EQ(filter_result.value(), &filters[1]);
117
118 filter_result = filter_map.GetFilterFromId(filter_id1);
119 ASSERT_TRUE(filter_result.ok());
120 EXPECT_EQ(filter_result.value(), &filters[0]);
121
122 const std::array<std::byte, cfg::kMaxFilterIdBytes> invalid_id{
123 std::byte(0xd0), std::byte(0x1c), std::byte(0xe7), std::byte(0xea)};
124 filter_result = filter_map.GetFilterFromId(invalid_id);
125 ASSERT_EQ(filter_result.status(), Status::NotFound());
126 }
127
TEST(Filter,UpdateFilterRules)128 TEST(Filter, UpdateFilterRules) {
129 const std::array<std::byte, cfg::kMaxFilterIdBytes> filter_id{
130 std::byte(0xba), std::byte(0x1d), std::byte(0xba), std::byte(0xb1)};
131 std::array<Filter::Rule, 4> rules;
132 const std::array<Filter::Rule, 4> new_rules{{
133 {
134 .action = Filter::Rule::Action::kKeep,
135 .level_greater_than_or_equal = FilterRule::Level::DEBUG_LEVEL,
136 .any_flags_set = 0x0f,
137 .module_equals{std::byte(123)},
138 .thread_equals{},
139 },
140 {
141 .action = Filter::Rule::Action::kInactive,
142 .level_greater_than_or_equal = FilterRule::Level::ANY_LEVEL,
143 .any_flags_set = 0xef,
144 .module_equals{},
145 .thread_equals{std::byte('L'), std::byte('O'), std::byte('G')},
146 },
147 {
148 .action = Filter::Rule::Action::kKeep,
149 .level_greater_than_or_equal = FilterRule::Level::INFO_LEVEL,
150 .any_flags_set = 0x1234,
151 .module_equals{std::byte(99)},
152 .thread_equals{},
153 },
154 {
155 .action = Filter::Rule::Action::kDrop,
156 .level_greater_than_or_equal = FilterRule::Level::ANY_LEVEL,
157 .any_flags_set = 0,
158 .module_equals{std::byte(4)},
159 .thread_equals{std::byte('P'),
160 std::byte('O'),
161 std::byte('W'),
162 std::byte('E'),
163 std::byte('R')},
164 },
165 }};
166
167 Filter filter(filter_id, rules);
168 const Filter new_filter(filter_id,
169 const_cast<std::array<Filter::Rule, 4>&>(new_rules));
170 std::byte buffer[256];
171 auto encode_result = EncodeFilter(new_filter, buffer);
172 ASSERT_EQ(encode_result.status(), OkStatus());
173 EXPECT_EQ(filter.UpdateRulesFromProto(encode_result.value()), OkStatus());
174
175 size_t i = 0;
176 for (const auto& rule : filter.rules()) {
177 VerifyRule(rule, new_rules[i++]);
178 }
179
180 // A new filter with no rules should clear filter.
181 const Filter empty_filter(filter_id, {});
182 std::memset(buffer, 0, sizeof(buffer));
183 encode_result = EncodeFilter(empty_filter, buffer);
184 ASSERT_EQ(encode_result.status(), OkStatus());
185 EXPECT_EQ(filter.UpdateRulesFromProto(encode_result.value()), OkStatus());
186 const Filter::Rule empty_rule{
187 .action = Filter::Rule::Action::kInactive,
188 .level_greater_than_or_equal = FilterRule::Level::ANY_LEVEL,
189 .any_flags_set = 0,
190 .module_equals{},
191 .thread_equals{},
192 };
193 for (const auto& rule : filter.rules()) {
194 VerifyRule(rule, empty_rule);
195 }
196 EXPECT_TRUE(empty_filter.rules().empty());
197
198 // Passing a new filter with less rules.
199 const std::array<Filter::Rule, 2> few_rules{{
200 {
201 .action = Filter::Rule::Action::kInactive,
202 .level_greater_than_or_equal = FilterRule::Level::ANY_LEVEL,
203 .any_flags_set = 0xef,
204 .module_equals{},
205 .thread_equals{},
206 },
207 {
208 .action = Filter::Rule::Action::kKeep,
209 .level_greater_than_or_equal = FilterRule::Level::INFO_LEVEL,
210 .any_flags_set = 0x1234,
211 .module_equals{std::byte(99)},
212 .thread_equals{std::byte('P'),
213 std::byte('O'),
214 std::byte('W'),
215 std::byte('E'),
216 std::byte('R')},
217 },
218 }};
219 const Filter filter_few_rules(
220 filter_id, const_cast<std::array<Filter::Rule, 2>&>(few_rules));
221 std::memset(buffer, 0, sizeof(buffer));
222 encode_result = EncodeFilter(filter_few_rules, buffer);
223 ASSERT_EQ(encode_result.status(), OkStatus());
224 EXPECT_EQ(filter.UpdateRulesFromProto(encode_result.value()), OkStatus());
225 i = 0;
226 for (const auto& rule : filter.rules()) {
227 if (i >= few_rules.size()) {
228 VerifyRule(rule, empty_rule);
229 } else {
230 VerifyRule(rule, few_rules[i++]);
231 }
232 }
233
234 // Passing a new filter with extra rules.
235 const std::array<Filter::Rule, 6> extra_rules{{
236 {
237 .action = Filter::Rule::Action::kKeep,
238 .level_greater_than_or_equal = FilterRule::Level::DEBUG_LEVEL,
239 .any_flags_set = 0x0f,
240 .module_equals{std::byte(123)},
241 .thread_equals{std::byte('P'),
242 std::byte('O'),
243 std::byte('W'),
244 std::byte('E'),
245 std::byte('R')},
246 },
247 {
248 .action = Filter::Rule::Action::kInactive,
249 .level_greater_than_or_equal = FilterRule::Level::ANY_LEVEL,
250 .any_flags_set = 0xef,
251 .module_equals{},
252 .thread_equals{},
253 },
254 {
255 .action = Filter::Rule::Action::kInactive,
256 .level_greater_than_or_equal = FilterRule::Level::ANY_LEVEL,
257 .any_flags_set = 0xef,
258 .module_equals{},
259 .thread_equals{},
260 },
261 {
262 .action = Filter::Rule::Action::kKeep,
263 .level_greater_than_or_equal = FilterRule::Level::INFO_LEVEL,
264 .any_flags_set = 0x1234,
265 .module_equals{std::byte(99)},
266 .thread_equals{std::byte('L'), std::byte('O'), std::byte('G')},
267 },
268 {
269 .action = Filter::Rule::Action::kDrop,
270 .level_greater_than_or_equal = FilterRule::Level::ANY_LEVEL,
271 .any_flags_set = 0,
272 .module_equals{std::byte(4)},
273 .thread_equals{std::byte('L'), std::byte('O'), std::byte('G')},
274 },
275 {
276 .action = Filter::Rule::Action::kKeep,
277 .level_greater_than_or_equal = FilterRule::Level::INFO_LEVEL,
278 .any_flags_set = 0x1234,
279 .module_equals{std::byte('M'),
280 std::byte('0'),
281 std::byte('L'),
282 std::byte('O'),
283 std::byte('G')},
284 .thread_equals{},
285 },
286 }};
287 const Filter filter_extra_rules(
288 filter_id, const_cast<std::array<Filter::Rule, 6>&>(extra_rules));
289 std::memset(buffer, 0, sizeof(buffer));
290 encode_result = EncodeFilter(filter_extra_rules, buffer);
291 ASSERT_EQ(encode_result.status(), OkStatus());
292 EXPECT_EQ(filter.UpdateRulesFromProto(encode_result.value()), OkStatus());
293 i = 0;
294 for (const auto& rule : filter.rules()) {
295 VerifyRule(rule, extra_rules[i++]);
296 }
297
298 // A filter with no rules buffer cannot get rules updated.
299 Filter filter_no_rules(filter_id, {});
300 EXPECT_EQ(filter_no_rules.UpdateRulesFromProto(encode_result.value()),
301 Status::FailedPrecondition());
302 }
303
TEST(FilterTest,FilterLogsRuleDefaultDrop)304 TEST(FilterTest, FilterLogsRuleDefaultDrop) {
305 const std::array<Filter::Rule, 2> rules{{
306 {
307 .action = Filter::Rule::Action::kKeep,
308 .level_greater_than_or_equal = FilterRule::Level::INFO_LEVEL,
309 .any_flags_set = kSampleFlags,
310 .module_equals{kSampleModuleLittleEndian.begin(),
311 kSampleModuleLittleEndian.end()},
312 .thread_equals{kSampleThread.begin(), kSampleThread.end()},
313 },
314 // This rule catches all logs.
315 {
316 .action = Filter::Rule::Action::kDrop,
317 .level_greater_than_or_equal = FilterRule::Level::ANY_LEVEL,
318 .any_flags_set = 0,
319 .module_equals = {},
320 .thread_equals{},
321 },
322 }};
323 const std::array<std::byte, cfg::kMaxFilterIdBytes> filter_id{
324 std::byte(0xfe), std::byte(0xed), std::byte(0xba), std::byte(0xb1)};
325 const Filter filter(filter_id,
326 const_cast<std::array<Filter::Rule, 2>&>(rules));
327
328 std::array<std::byte, 50> buffer;
329 const Result<ConstByteSpan> log_entry_info =
330 EncodeLogEntry<PW_LOG_LEVEL_INFO, kSampleModule, kSampleFlags>(
331 kSampleMessage, buffer, kSampleThread);
332 ASSERT_EQ(log_entry_info.status(), OkStatus());
333 EXPECT_FALSE(filter.ShouldDropLog(log_entry_info.value()));
334
335 buffer.fill(std::byte(0));
336 const Result<ConstByteSpan> log_entry_debug =
337 EncodeLogEntry<PW_LOG_LEVEL_DEBUG, kSampleModule, kSampleFlags>(
338 kSampleMessage, buffer, kSampleThread);
339 ASSERT_EQ(log_entry_debug.status(), OkStatus());
340 EXPECT_TRUE(filter.ShouldDropLog(log_entry_debug.value()));
341
342 buffer.fill(std::byte(0));
343 const Result<ConstByteSpan> log_entry_warn =
344 EncodeLogEntry<PW_LOG_LEVEL_WARN, kSampleModule, kSampleFlags>(
345 kSampleMessage, buffer, kSampleThread);
346 ASSERT_EQ(log_entry_warn.status(), OkStatus());
347 EXPECT_FALSE(filter.ShouldDropLog(log_entry_warn.value()));
348
349 buffer.fill(std::byte(0));
350 const Result<ConstByteSpan> log_entry_error =
351 EncodeLogEntry<PW_LOG_LEVEL_ERROR, kSampleModule, kSampleFlags>(
352 kSampleMessage, buffer, kSampleThread);
353 ASSERT_EQ(log_entry_error.status(), OkStatus());
354 EXPECT_FALSE(filter.ShouldDropLog(log_entry_error.value()));
355
356 buffer.fill(std::byte(0));
357 const Result<ConstByteSpan> log_entry_info_different =
358 EncodeLogEntry<PW_LOG_LEVEL_INFO, 0, 0>(kSampleMessage, buffer, {});
359 ASSERT_EQ(log_entry_info_different.status(), OkStatus());
360 EXPECT_TRUE(filter.ShouldDropLog(log_entry_info_different.value()));
361 // Because the last rule catches all logs, the filter default action is not
362 // applied.
363 const Filter filter_default_drop(
364 filter_id, const_cast<std::array<Filter::Rule, 2>&>(rules));
365 EXPECT_TRUE(
366 filter_default_drop.ShouldDropLog(log_entry_info_different.value()));
367
368 buffer.fill(std::byte(0));
369 const Result<ConstByteSpan> log_entry_same_flags =
370 EncodeLogEntry<0, 0, kSampleFlags>(kSampleMessage, buffer, {});
371 ASSERT_EQ(log_entry_same_flags.status(), OkStatus());
372 EXPECT_TRUE(filter.ShouldDropLog(log_entry_same_flags.value()));
373
374 buffer.fill(std::byte(0));
375 const Result<ConstByteSpan> log_entry_same_module =
376 EncodeLogEntry<0, kSampleModule, 0>(kSampleMessage, buffer, {});
377 ASSERT_EQ(log_entry_same_module.status(), OkStatus());
378 EXPECT_TRUE(filter.ShouldDropLog(log_entry_same_module.value()));
379
380 buffer.fill(std::byte(0));
381 const Result<ConstByteSpan> log_entry_same_thread =
382 EncodeLogEntry<0, 0, 0>(kSampleMessage, buffer, kSampleThread);
383 ASSERT_EQ(log_entry_same_thread.status(), OkStatus());
384 EXPECT_TRUE(filter.ShouldDropLog(log_entry_same_thread.value()));
385 }
386
TEST(FilterTest,FilterLogsKeepLogsWhenNoRuleMatches)387 TEST(FilterTest, FilterLogsKeepLogsWhenNoRuleMatches) {
388 // There is no rule that catches all logs.
389 const std::array<Filter::Rule, 1> rules{{
390 {
391 .action = Filter::Rule::Action::kKeep,
392 .level_greater_than_or_equal = FilterRule::Level::INFO_LEVEL,
393 .any_flags_set = kSampleFlags,
394 .module_equals = {kSampleModuleLittleEndian.begin(),
395 kSampleModuleLittleEndian.end()},
396 .thread_equals = {kSampleThread.begin(), kSampleThread.end()},
397 },
398 }};
399
400 // Filters should not share rules if they are mutable, to avoid race
401 // conditions.
402 const std::array<std::byte, cfg::kMaxFilterIdBytes> filter_id{
403 std::byte(0xfe), std::byte(0xed), std::byte(0xba), std::byte(0xb1)};
404 const Filter filter(filter_id,
405 const_cast<std::array<Filter::Rule, 1>&>(rules));
406
407 std::array<std::byte, 50> buffer;
408 const Result<ConstByteSpan> log_entry_info =
409 EncodeLogEntry<PW_LOG_LEVEL_INFO, kSampleModule, kSampleFlags>(
410 kSampleMessage, buffer, kSampleThread);
411 ASSERT_EQ(log_entry_info.status(), OkStatus());
412 EXPECT_FALSE(filter.ShouldDropLog(log_entry_info.value()));
413
414 buffer.fill(std::byte(0));
415 const Result<ConstByteSpan> log_entry_debug =
416 EncodeLogEntry<PW_LOG_LEVEL_DEBUG, kSampleModule, kSampleFlags>(
417 kSampleMessage, buffer, kSampleThread);
418 ASSERT_EQ(log_entry_debug.status(), OkStatus());
419 EXPECT_FALSE(filter.ShouldDropLog(log_entry_debug.value()));
420
421 buffer.fill(std::byte(0));
422 const Result<ConstByteSpan> log_entry_warn =
423 EncodeLogEntry<PW_LOG_LEVEL_WARN, kSampleModule, kSampleFlags>(
424 kSampleMessage, buffer, kSampleThread);
425 ASSERT_EQ(log_entry_warn.status(), OkStatus());
426 EXPECT_FALSE(filter.ShouldDropLog(log_entry_warn.value()));
427
428 buffer.fill(std::byte(0));
429 const Result<ConstByteSpan> log_entry_error =
430 EncodeLogEntry<PW_LOG_LEVEL_ERROR, kSampleModule, kSampleFlags>(
431 kSampleMessage, buffer, kSampleThread);
432 ASSERT_EQ(log_entry_error.status(), OkStatus());
433 EXPECT_FALSE(filter.ShouldDropLog(log_entry_error.value()));
434
435 buffer.fill(std::byte(0));
436 const Result<ConstByteSpan> log_entry_info_different =
437 EncodeLogEntry<PW_LOG_LEVEL_INFO, 0, 0>(kSampleMessage, buffer, {});
438 ASSERT_EQ(log_entry_info_different.status(), OkStatus());
439 EXPECT_FALSE(filter.ShouldDropLog(log_entry_info_different.value()));
440
441 buffer.fill(std::byte(0));
442 const Result<ConstByteSpan> log_entry_same_flags =
443 EncodeLogEntry<0, 0, kSampleFlags>(kSampleMessage, buffer, {});
444 ASSERT_EQ(log_entry_same_flags.status(), OkStatus());
445 EXPECT_FALSE(filter.ShouldDropLog(log_entry_same_flags.value()));
446
447 buffer.fill(std::byte(0));
448 const Result<ConstByteSpan> log_entry_same_module =
449 EncodeLogEntry<0, kSampleModule, 0>(kSampleMessage, buffer, {});
450 ASSERT_EQ(log_entry_same_module.status(), OkStatus());
451 EXPECT_FALSE(filter.ShouldDropLog(log_entry_same_module.value()));
452
453 buffer.fill(std::byte(0));
454 const Result<ConstByteSpan> log_entry_same_thread =
455 EncodeLogEntry<0, 0, 0>(kSampleMessage, buffer, kSampleThread);
456 ASSERT_EQ(log_entry_same_thread.status(), OkStatus());
457 EXPECT_FALSE(filter.ShouldDropLog(log_entry_same_thread.value()));
458 }
459
TEST(FilterTest,FilterLogsKeepLogsWhenRulesEmpty)460 TEST(FilterTest, FilterLogsKeepLogsWhenRulesEmpty) {
461 // Filters should not share rules if they are mutable, to avoid race
462 // conditions.
463 const std::array<std::byte, cfg::kMaxFilterIdBytes> filter_id{
464 std::byte(0xfe), std::byte(0xed), std::byte(0xba), std::byte(0xb1)};
465 const Filter filter(filter_id, {});
466
467 std::array<std::byte, 50> buffer;
468 const Result<ConstByteSpan> log_entry_info =
469 EncodeLogEntry<PW_LOG_LEVEL_INFO, kSampleModule, kSampleFlags>(
470 kSampleMessage, buffer, kSampleThread);
471 ASSERT_EQ(log_entry_info.status(), OkStatus());
472 EXPECT_FALSE(filter.ShouldDropLog(log_entry_info.value()));
473
474 buffer.fill(std::byte(0));
475 const Result<ConstByteSpan> log_entry_debug =
476 EncodeLogEntry<PW_LOG_LEVEL_DEBUG, kSampleModule, kSampleFlags>(
477 kSampleMessage, buffer, kSampleThread);
478 ASSERT_EQ(log_entry_debug.status(), OkStatus());
479 EXPECT_FALSE(filter.ShouldDropLog(log_entry_debug.value()));
480
481 buffer.fill(std::byte(0));
482 const Result<ConstByteSpan> log_entry_warn =
483 EncodeLogEntry<PW_LOG_LEVEL_WARN, kSampleModule, kSampleFlags>(
484 kSampleMessage, buffer, kSampleThread);
485 ASSERT_EQ(log_entry_warn.status(), OkStatus());
486 EXPECT_FALSE(filter.ShouldDropLog(log_entry_warn.value()));
487
488 buffer.fill(std::byte(0));
489 const Result<ConstByteSpan> log_entry_error =
490 EncodeLogEntry<PW_LOG_LEVEL_ERROR, kSampleModule, kSampleFlags>(
491 kSampleMessage, buffer, kSampleThread);
492 ASSERT_EQ(log_entry_error.status(), OkStatus());
493 EXPECT_FALSE(filter.ShouldDropLog(log_entry_error.value()));
494
495 buffer.fill(std::byte(0));
496 const Result<ConstByteSpan> log_entry_info_different =
497 EncodeLogEntry<PW_LOG_LEVEL_INFO, 0, 0>(kSampleMessage, buffer, {});
498 ASSERT_EQ(log_entry_info_different.status(), OkStatus());
499 EXPECT_FALSE(filter.ShouldDropLog(log_entry_info_different.value()));
500
501 buffer.fill(std::byte(0));
502 const Result<ConstByteSpan> log_entry_same_flags =
503 EncodeLogEntry<0, 0, kSampleFlags>(kSampleMessage, buffer, {});
504 ASSERT_EQ(log_entry_same_flags.status(), OkStatus());
505 EXPECT_FALSE(filter.ShouldDropLog(log_entry_same_flags.value()));
506
507 buffer.fill(std::byte(0));
508 const Result<ConstByteSpan> log_entry_same_module =
509 EncodeLogEntry<0, kSampleModule, 0>(kSampleMessage, buffer, {});
510 ASSERT_EQ(log_entry_same_module.status(), OkStatus());
511 EXPECT_FALSE(filter.ShouldDropLog(log_entry_same_module.value()));
512
513 buffer.fill(std::byte(0));
514 const Result<ConstByteSpan> log_entry_same_thread =
515 EncodeLogEntry<0, 0, 0>(kSampleMessage, buffer, kSampleThread);
516 ASSERT_EQ(log_entry_same_thread.status(), OkStatus());
517 EXPECT_FALSE(filter.ShouldDropLog(log_entry_same_thread.value()));
518 }
519
TEST(FilterTest,FilterLogsFirstRuleWins)520 TEST(FilterTest, FilterLogsFirstRuleWins) {
521 const std::array<Filter::Rule, 2> rules{{
522 {
523 .action = Filter::Rule::Action::kKeep,
524 .level_greater_than_or_equal = FilterRule::Level::INFO_LEVEL,
525 .any_flags_set = kSampleFlags,
526 .module_equals = {kSampleModuleLittleEndian.begin(),
527 kSampleModuleLittleEndian.end()},
528 .thread_equals = {kSampleThread.begin(), kSampleThread.end()},
529 },
530 {
531 .action = Filter::Rule::Action::kDrop,
532 .level_greater_than_or_equal = FilterRule::Level::INFO_LEVEL,
533 .any_flags_set = kSampleFlags,
534 .module_equals = {kSampleModuleLittleEndian.begin(),
535 kSampleModuleLittleEndian.end()},
536 .thread_equals = {kSampleThread.begin(), kSampleThread.end()},
537 },
538 }};
539 const std::array<Filter::Rule, 2> rules_reversed{{
540 {
541 .action = Filter::Rule::Action::kDrop,
542 .level_greater_than_or_equal = FilterRule::Level::INFO_LEVEL,
543 .any_flags_set = kSampleFlags,
544 .module_equals = {kSampleModuleLittleEndian.begin(),
545 kSampleModuleLittleEndian.end()},
546 .thread_equals = {kSampleThread.begin(), kSampleThread.end()},
547 },
548 {
549 .action = Filter::Rule::Action::kKeep,
550 .level_greater_than_or_equal = FilterRule::Level::INFO_LEVEL,
551 .any_flags_set = kSampleFlags,
552 .module_equals = {kSampleModuleLittleEndian.begin(),
553 kSampleModuleLittleEndian.end()},
554 .thread_equals = {kSampleThread.begin(), kSampleThread.end()},
555 },
556 }};
557 const std::array<std::byte, cfg::kMaxFilterIdBytes> filter_id1{
558 std::byte(0xfe), std::byte(0xed), std::byte(0xba), std::byte(0xb1)};
559 const std::array<std::byte, cfg::kMaxFilterIdBytes> filter_id2{
560 std::byte(0), std::byte(0), std::byte(0), std::byte(2)};
561 const Filter filter(filter_id1,
562 const_cast<std::array<Filter::Rule, 2>&>(rules));
563 const Filter filter_reverse_rules(
564 filter_id2, const_cast<std::array<Filter::Rule, 2>&>(rules_reversed));
565
566 std::array<std::byte, 50> buffer;
567 const Result<ConstByteSpan> log_entry_info =
568 EncodeLogEntry<PW_LOG_LEVEL_INFO, kSampleModule, kSampleFlags>(
569 kSampleMessage, buffer, kSampleThread);
570 ASSERT_EQ(log_entry_info.status(), OkStatus());
571 EXPECT_FALSE(filter.ShouldDropLog(log_entry_info.value()));
572 EXPECT_TRUE(filter_reverse_rules.ShouldDropLog(log_entry_info.value()));
573 }
574
TEST(FilterTest,DropFilterRuleDueToThreadName)575 TEST(FilterTest, DropFilterRuleDueToThreadName) {
576 const std::array<std::byte, cfg::kMaxThreadNameBytes - 7> kDropThread = {
577 std::byte('L'), std::byte('O'), std::byte('G')};
578 const std::array<Filter::Rule, 2> rules{{
579 {
580 .action = Filter::Rule::Action::kKeep,
581 .level_greater_than_or_equal = FilterRule::Level::INFO_LEVEL,
582 .any_flags_set = kSampleFlags,
583 .module_equals = {kSampleModuleLittleEndian.begin(),
584 kSampleModuleLittleEndian.end()},
585 .thread_equals = {kDropThread.begin(), kDropThread.end()},
586 },
587 {
588 .action = Filter::Rule::Action::kDrop,
589 .level_greater_than_or_equal = FilterRule::Level::INFO_LEVEL,
590 .any_flags_set = kSampleFlags,
591 .module_equals = {kSampleModuleLittleEndian.begin(),
592 kSampleModuleLittleEndian.end()},
593 .thread_equals = {kSampleThread.begin(), kSampleThread.end()},
594 },
595 }};
596
597 const std::array<Filter::Rule, 2> drop_rule{{
598 {
599 .action = Filter::Rule::Action::kDrop,
600 .level_greater_than_or_equal = FilterRule::Level::INFO_LEVEL,
601 .any_flags_set = kSampleFlags,
602 .module_equals = {kSampleModuleLittleEndian.begin(),
603 kSampleModuleLittleEndian.end()},
604 .thread_equals = {kDropThread.begin(), kDropThread.end()},
605 },
606 }};
607
608 const std::array<std::byte, cfg::kMaxFilterIdBytes> filter_id1{
609 std::byte(0xba), std::byte(0x1d), std::byte(0xba), std::byte(0xb1)};
610 // A filter's thread_equals name that does and does not match the log's thread
611 // name.
612 const Filter filter(filter_id1,
613 const_cast<std::array<Filter::Rule, 2>&>(rules));
614 const std::array<std::byte, cfg::kMaxFilterIdBytes> filter_id2{
615 std::byte(0), std::byte(0), std::byte(0), std::byte(2)};
616 // A filter's thread_equals name that does not match the log's thread name.
617 const Filter filter_with_unregistered_filter_rule(
618 filter_id2, const_cast<std::array<Filter::Rule, 2>&>(drop_rule));
619 std::array<std::byte, 50> buffer;
620 const Result<ConstByteSpan> log_entry_thread =
621 EncodeLogEntry<PW_LOG_LEVEL_INFO, kSampleModule, kSampleFlags>(
622 kSampleMessage, buffer, kSampleThread);
623 ASSERT_EQ(log_entry_thread.status(), OkStatus());
624 // Set filter rules to kDrop to showcase the output difference.
625 // Drop_rule not being dropped, while rules is dropped successfully.
626 EXPECT_TRUE(filter.ShouldDropLog(log_entry_thread.value()));
627 EXPECT_FALSE(filter_with_unregistered_filter_rule.ShouldDropLog(
628 log_entry_thread.value()));
629 }
630
TEST(FilterTest,UpdateFilterWithLargeThreadNamePasses)631 TEST(FilterTest, UpdateFilterWithLargeThreadNamePasses) {
632 // Threads are limited to a size of kMaxThreadNameBytes.
633 // However, the excess bytes will not be in the updated rules.
634 const std::array<std::byte, cfg::kMaxThreadNameBytes + 1>
635 kThreadNameLongerThanAllowed = {
636 std::byte('L'),
637 std::byte('O'),
638 std::byte('C'),
639 std::byte('A'),
640 std::byte('L'),
641 std::byte('E'),
642 std::byte('G'),
643 std::byte('R'),
644 std::byte('E'),
645 std::byte('S'),
646 std::byte('S'),
647 };
648
649 const std::array<Filter::Rule, 2> rule{{
650 {
651 .action = Filter::Rule::Action::kKeep,
652 .level_greater_than_or_equal = FilterRule::Level::INFO_LEVEL,
653 .any_flags_set = kSampleFlags,
654 .module_equals = {kSampleModuleLittleEndian.begin(),
655 kSampleModuleLittleEndian.end()},
656 .thread_equals = {kThreadNameLongerThanAllowed.begin(),
657 kThreadNameLongerThanAllowed.end()},
658 },
659 {
660 .action = Filter::Rule::Action::kKeep,
661 .level_greater_than_or_equal = FilterRule::Level::INFO_LEVEL,
662 .any_flags_set = kSampleFlags,
663 .module_equals = {kSampleModuleLittleEndian.begin(),
664 kSampleModuleLittleEndian.end()},
665 .thread_equals = {kSampleThread.begin(), kSampleThread.end()},
666 },
667 }};
668
669 const std::array<std::byte, cfg::kMaxFilterIdBytes> filter_id{
670 std::byte(0xba), std::byte(0x1d), std::byte(0xba), std::byte(0xb1)};
671 Filter filter(filter_id, const_cast<std::array<Filter::Rule, 2>&>(rule));
672 std::byte buffer[256];
673 auto encode_result = EncodeFilter(filter, buffer);
674 ASSERT_EQ(encode_result.status(), OkStatus());
675 EXPECT_EQ(filter.UpdateRulesFromProto(encode_result.value()), OkStatus());
676 size_t i = 0;
677 for (const auto& rules : filter.rules()) {
678 VerifyRule(rules, rule[i++]);
679 }
680 }
681
TEST(FilterTest,UpdateFilterWithLargeThreadNameFails)682 TEST(FilterTest, UpdateFilterWithLargeThreadNameFails) {
683 const std::array<Filter::Rule, 1> rule_with_more_than_ten_bytes{{{
684 .action = Filter::Rule::Action::kKeep,
685 .level_greater_than_or_equal = FilterRule::Level::INFO_LEVEL,
686 .any_flags_set = kSampleFlags,
687 .module_equals = {kSampleModuleLittleEndian.begin(),
688 kSampleModuleLittleEndian.end()},
689 .thread_equals = {kSampleThread.begin(), kSampleThread.end()},
690 }}};
691 const std::array<std::byte, cfg::kMaxFilterIdBytes> filter_id{
692 std::byte(0xba), std::byte(0x1d), std::byte(0xba), std::byte(0xb1)};
693 Filter filter(
694 filter_id,
695 const_cast<std::array<Filter::Rule, 1>&>(rule_with_more_than_ten_bytes));
696 std::byte buffer[256];
697 log::pwpb::Filter::MemoryEncoder encoder(buffer);
698 {
699 std::array<const std::byte, cfg::kMaxThreadNameBytes + 1>
700 kThreadNameLongerThanAllowed = {
701 std::byte('L'),
702 std::byte('O'),
703 std::byte('C'),
704 std::byte('A'),
705 std::byte('L'),
706 std::byte('E'),
707 std::byte('G'),
708 std::byte('R'),
709 std::byte('E'),
710 std::byte('S'),
711 std::byte('S'),
712 };
713 // Stream encoder writes to the buffer when it goes out of scope.
714 FilterRule::StreamEncoder rule_encoder = encoder.GetRuleEncoder();
715 ASSERT_EQ(rule_encoder.WriteThreadEquals(kThreadNameLongerThanAllowed),
716 OkStatus());
717 }
718 EXPECT_EQ(filter.UpdateRulesFromProto(ConstByteSpan(encoder)),
719 Status::InvalidArgument());
720 }
721 } // namespace
722 } // namespace pw::log_rpc
723