diff --git a/crash_reporter/Makefile b/crash_reporter/Makefile index 648308f6f..4a0f488f6 100644 --- a/crash_reporter/Makefile +++ b/crash_reporter/Makefile @@ -12,6 +12,7 @@ PKG_CONFIG ?= pkg-config CRASH_OBJS = \ crash_collector.o \ kernel_collector.o \ + kernel_warning_collector.o \ udev_collector.o \ unclean_shutdown_collector.o \ user_collector.o @@ -44,6 +45,18 @@ clean: CLEAN(list_proxies) all: CXX_BINARY(list_proxies) +# -- Executable warn_collector -- + +CC_BINARY(warn_collector): LDLIBS += -lfl -lmetrics +CC_BINARY(warn_collector): lex.yy.o +clean: CLEAN(warn_collector) +all: CC_BINARY(warn_collector) +lex.yy.c: $(SRC)/warn_collector.l + flex $< +clean: CLEAN(lex.yy.o) +$(eval $(call add_object_rules,lex.yy.o,CC,c)) + + # -- Unit Tests -- # Uncomment these to just run specific test(s). @@ -65,8 +78,10 @@ clean: CLEAN($(1)) tests: TEST(CXX_BINARY($(1))) endef +CC_BINARY(warn_collector_test): warn_collector_test.o + # If $(TEST_BINS) is not already set, assume any filename matching *_test.cc # is a test. TEST_BINS ?= $(patsubst $(SRC)/%.cc,%,$(wildcard $(SRC)/*_test.cc)) $(foreach prog,$(TEST_BINS),$(eval $(call TEST_RULES_template,$(prog)))) - +tests: TEST(CC_BINARY(warn_collector_test)) diff --git a/crash_reporter/TEST_WARNING b/crash_reporter/TEST_WARNING new file mode 100644 index 000000000..64ad2e9a0 --- /dev/null +++ b/crash_reporter/TEST_WARNING @@ -0,0 +1,31 @@ +Apr 31 25:25:25 localhost kernel: [117959.226729] [] do_vfs_ioctl+0x469/0x4b3 +Apr 31 25:25:25 localhost kernel: [117959.226738] [] ? fsnotify_access+0x58/0x60 +Apr 31 25:25:25 localhost kernel: [117959.226747] [] ? vfs_read+0xad/0xd7 +Apr 31 25:25:25 localhost kernel: [117959.226756] [] sys_ioctl+0x56/0x7b +Apr 31 25:25:25 localhost kernel: [117959.226765] [] ? sys_read+0x43/0x73 +Apr 31 25:25:25 localhost kernel: [117959.226774] [] system_call_fastpath+0x16/0x1b +Apr 31 25:25:25 localhost kernel: [117959.226782] ---[ end trace f16822cad7406cec ]--- +Apr 31 25:25:25 localhost kernel: [117959.231085] ------------[ cut here ]------------ +Apr 31 25:25:25 localhost kernel: [117959.231100] WARNING: at /mnt/host/source/src/third_party/kernel/files/drivers/gpu/drm/i915/intel_dp.c:351 intel_dp_check_edp+0x6b/0xb9() +Apr 31 25:25:25 localhost kernel: [117959.231113] Hardware name: Link +Apr 31 25:25:25 localhost kernel: [117959.231117] eDP powered off while attempting aux channel communication. +Apr 31 25:25:25 localhost kernel: [117959.231240] Pid: 10508, comm: X Tainted: G WC 3.4.0 #1 +Apr 31 25:25:25 localhost kernel: [117959.231247] Call Trace: +Apr 31 25:25:25 localhost kernel: [117959.231393] [] ? fsnotify_access+0x58/0x60 +Apr 31 25:25:25 localhost kernel: [117959.231402] [] ? vfs_read+0xad/0xd7 +Apr 31 25:25:25 localhost kernel: [117959.231411] [] sys_ioctl+0x56/0x7b +Apr 31 25:25:25 localhost kernel: [117959.231420] [] ? sys_read+0x43/0x73 +Apr 31 25:25:25 localhost kernel: [117959.231431] [] system_call_fastpath+0x16/0x1b +Apr 31 25:25:25 localhost kernel: [117959.231439] ---[ end trace f16822cad7406ced ]--- +Apr 31 25:25:25 localhost kernel: [117959.231450] ------------[ cut here ]------------ +Apr 31 25:25:25 localhost kernel: [117959.231458] BARNING: at /mnt/host/source/src/third_party/kernel/files/drivers/gpu/drm/i915/intel_dp.c:351 intel_dp_check_edp+0x6b/0xb9() +Apr 31 25:25:25 localhost kernel: [117959.231458] ("BARNING" above is intentional) +Apr 31 25:25:25 localhost kernel: [117959.231471] Hardware name: Link +Apr 31 25:25:25 localhost kernel: [117959.231475] eDP powered off while attempting aux channel communication. +Apr 31 25:25:25 localhost kernel: [117959.231482] Modules linked in: nls_iso8859_1 nls_cp437 vfat fat rfcomm i2c_dev ath9k_btcoex snd_hda_codec_hdmi snd_hda_codec_ca0132 mac80211 snd_hda_intel ath9k_common_btcoex snd_hda_codec ath9k_hw_btcoex aesni_intel cryptd snd_hwdep ath snd_pcm aes_x86_64 isl29018(C) memconsole snd_timer snd_page_alloc industrialio(C) cfg80211 rtc_cmos nm10_gpio zram(C) zsmalloc(C) lzo_decompress lzo_compress fuse nf_conntrack_ipv6 nf_defrag_ipv6 ip6table_filter ip6_tables xt_mark option usb_wwan cdc_ether usbnet ath3k btusb bluetooth uvcvideo videobuf2_core videodev videobuf2_vmalloc videobuf2_memops joydev +Apr 31 25:25:25 localhost kernel: [117959.231588] Pid: 10508, comm: X Tainted: G WC 3.4.0 #1 +Apr 31 25:25:25 localhost kernel: [117959.231595] Call Trace: +Apr 31 25:25:25 localhost kernel: [117959.231601] [] warn_slowpath_common+0x83/0x9c +Apr 31 25:25:25 localhost kernel: [117959.231610] [] warn_slowpath_fmt+0x46/0x48 +Apr 31 25:25:25 localhost kernel: [117959.231620] [] intel_dp_check_edp+0x6b/0xb9 +Apr 31 25:25:25 localhost kernel: [117959.231629] [] ? warn_slowpath_fmt+ diff --git a/crash_reporter/crash_reporter.cc b/crash_reporter/crash_reporter.cc index e76c5c82f..d298108e5 100644 --- a/crash_reporter/crash_reporter.cc +++ b/crash_reporter/crash_reporter.cc @@ -15,6 +15,7 @@ #include "base/stringprintf.h" #include "chromeos/syslog_logging.h" #include "crash-reporter/kernel_collector.h" +#include "crash-reporter/kernel_warning_collector.h" #include "crash-reporter/udev_collector.h" #include "crash-reporter/unclean_shutdown_collector.h" #include "crash-reporter/user_collector.h" @@ -30,6 +31,7 @@ DEFINE_bool(crash_test, false, "Crash test"); DEFINE_string(user, "", "User crash info (pid:signal:exec_name)"); DEFINE_bool(unclean_check, true, "Check for unclean shutdown"); DEFINE_string(udev, "", "Udev event description (type:device:subsystem)"); +DEFINE_bool(kernel_warning, false, "Report collected kernel warning"); #pragma GCC diagnostic error "-Wstrict-aliasing" static const char kCrashCounterHistogram[] = "Logging.CrashCounter"; @@ -47,6 +49,7 @@ enum CrashKinds { kCrashKindUser = 2, kCrashKindKernel = 3, kCrashKindUdev = 4, + kCrashKindKernelWarning = 5, kCrashKindMax }; @@ -176,6 +179,17 @@ static int HandleUdevCrash(UdevCollector *udev_collector) { return 0; } +static int HandleKernelWarning(KernelWarningCollector + *kernel_warning_collector) { + // Accumulate logs to help in diagnosing failures during collection. + chromeos::LogToString(true); + bool handled = kernel_warning_collector->Collect(); + chromeos::LogToString(false); + if (!handled) + return 1; + return 0; +} + // Interactive/diagnostics mode for generating kernel crash signatures. static int GenerateKernelSignature(KernelCollector *kernel_collector) { std::string kcrash_contents; @@ -238,6 +252,9 @@ int main(int argc, char *argv[]) { UdevCollector udev_collector; udev_collector.Initialize(CountUdevCrash, IsFeedbackAllowed); + KernelWarningCollector kernel_warning_collector; + udev_collector.Initialize(CountUdevCrash, IsFeedbackAllowed); + if (FLAGS_init) { return Initialize(&kernel_collector, &user_collector, @@ -258,5 +275,9 @@ int main(int argc, char *argv[]) { return HandleUdevCrash(&udev_collector); } + if (FLAGS_kernel_warning) { + return HandleKernelWarning(&kernel_warning_collector); + } + return HandleUserCrash(&user_collector); } diff --git a/crash_reporter/kernel_warning_collector.cc b/crash_reporter/kernel_warning_collector.cc new file mode 100644 index 000000000..b504f497b --- /dev/null +++ b/crash_reporter/kernel_warning_collector.cc @@ -0,0 +1,97 @@ +// Copyright (c) 2012 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "crash-reporter/kernel_warning_collector.h" + +#include "base/file_util.h" +#include "base/logging.h" +#include "base/string_number_conversions.h" +#include "base/string_util.h" +#include "base/stringprintf.h" + +namespace { +const char kExecName[] = "kernel-warning"; +const char kKernelWarningSignatureKey[] = "sig"; +const char kKernelWarningPath[] = "/var/run/kwarn/warning"; +const pid_t kKernelPid = 0; +const uid_t kRootUid = 0; +} // namespace + +KernelWarningCollector::KernelWarningCollector() { +} + +KernelWarningCollector::~KernelWarningCollector() { +} + +bool KernelWarningCollector::LoadKernelWarning(std::string *content, + std::string *hash_string) { + FilePath kernel_warning_path(kKernelWarningPath); + if (!file_util::ReadFileToString(kernel_warning_path, content)) { + LOG(ERROR) << "Could not open " << kKernelWarningPath; + return false; + } + /* Verify that the first line contains an 8-digit hex hash. */ + *hash_string = content->substr(0, 8); + std::vector output; + if (!base::HexStringToBytes(*hash_string, &output)) { + LOG(ERROR) << "Bad hash " << *hash_string << " in " << kKernelWarningPath; + return false; + } + return true; +} + +bool KernelWarningCollector::Collect() { + std::string reason = "normal collection"; + bool feedback = true; + if (IsDeveloperImage()) { + reason = "always collect from developer builds"; + feedback = true; + } else if (!is_feedback_allowed_function_()) { + reason = "no user consent"; + feedback = false; + } + + LOG(INFO) << "Processing kernel warning: " << reason; + + if (!feedback) { + return true; + } + + std::string kernel_warning; + std::string warning_hash; + if (!LoadKernelWarning(&kernel_warning, &warning_hash)) { + return true; + } + + FilePath root_crash_directory; + if (!GetCreatedCrashDirectoryByEuid(kRootUid, &root_crash_directory, NULL)) { + return true; + } + + std::string dump_basename = + FormatDumpBasename(kExecName, time(NULL), kKernelPid); + FilePath kernel_crash_path = root_crash_directory.Append( + StringPrintf("%s.kcrash", dump_basename.c_str())); + + // We must use WriteNewFile instead of file_util::WriteFile as we + // do not want to write with root access to a symlink that an attacker + // might have created. + if (WriteNewFile(kernel_crash_path, + kernel_warning.data(), + kernel_warning.length()) != + static_cast(kernel_warning.length())) { + LOG(INFO) << "Failed to write kernel warning to " + << kernel_crash_path.value().c_str(); + return true; + } + + AddCrashMetaData(kKernelWarningSignatureKey, warning_hash); + WriteCrashMetaData( + root_crash_directory.Append( + StringPrintf("%s.meta", dump_basename.c_str())), + kExecName, kernel_crash_path.value()); + + LOG(INFO) << "Stored kernel warning into " << kernel_crash_path.value(); + return true; +} diff --git a/crash_reporter/kernel_warning_collector.h b/crash_reporter/kernel_warning_collector.h new file mode 100644 index 000000000..2f8c793b8 --- /dev/null +++ b/crash_reporter/kernel_warning_collector.h @@ -0,0 +1,31 @@ +// Copyright (c) 2013 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef _CRASH_REPORTER_KERNEL_WARNING_COLLECTOR_H_ +#define _CRASH_REPORTER_KERNEL_WARNING_COLLECTOR_H_ + +#include // for FRIEND_TEST +#include + +#include "crash-reporter/crash_collector.h" + +// Kernel warning collector. +class KernelWarningCollector : public CrashCollector { + public: + KernelWarningCollector(); + + virtual ~KernelWarningCollector(); + + // Collects warning. + bool Collect(); + + private: + friend class KernelWarningCollectorTest; + FRIEND_TEST(KernelWarningCollectorTest, CollectOK); + + // Reads the full content of the kernel warn dump and the warning hash. + bool LoadKernelWarning(std::string *hash, std::string *content); +}; + +#endif // _CRASH_REPORTER_KERNEL_WARNING_COLLECTOR_H_ diff --git a/crash_reporter/warn_collector.l b/crash_reporter/warn_collector.l new file mode 100644 index 000000000..d0977dcf6 --- /dev/null +++ b/crash_reporter/warn_collector.l @@ -0,0 +1,300 @@ +/* Copyright (c) 2013 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + * + * This flex program reads /var/log/messages as it grows and saves kernel + * warnings to files. It keeps track of warnings it has seen (based on + * file/line only, ignoring differences in the stack trace), and reports only + * the first warning of each kind, but maintains a count of all warnings by + * using their hashes as buckets in a UMA sparse histogram. It also invokes + * the crash collector, which collects the warnings and prepares them for later + * shipment to the crash server. + */ + +%{ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "metrics/c_metrics_library.h" + +int WarnStart(void); +void WarnEnd(void); +void WarnInput(char *buf, int *result, size_t max_size); + +#define YY_INPUT(buf, result, max_size) WarnInput(buf, &result, max_size) + +%} + +/* Define a few useful regular expressions. */ + +D [0-9] +PREFIX .*" kernel: [ "*{D}+"."{D}+"]" +CUT_HERE {PREFIX}" ------------[ cut here".* +WARNING {PREFIX}" WARNING: at " +END_TRACE {PREFIX}" ---[ end trace".* + +/* Use exclusive start conditions. */ +%x PRE_WARN WARN + +%% + /* The scanner itself. */ + +^{CUT_HERE}\n{WARNING} BEGIN(PRE_WARN); +.|\n /* ignore all other input in state 0 */ +.*\n if (WarnStart()) { + BEGIN(WARN); ECHO; + } else { + BEGIN(0); + } + + /* Assume the warning ends at the "end trace" line */ +^{END_TRACE}\n ECHO; BEGIN(0); WarnEnd(); +^.*\n ECHO; + +%% + +#define HASH_BITMAP_SIZE (1 << 15) /* size in bits */ +#define HASH_BITMAP_MASK (HASH_BITMAP_SIZE - 1) + +const char warn_hist_name[] = "Platform.KernelWarningHashes"; +uint32_t hash_bitmap[HASH_BITMAP_SIZE / 32]; +CMetricsLibrary metrics_library; + +const char *prog_name; /* the name of this program */ +int yyin_fd; /* instead of FILE *yyin to avoid buffering */ +int i_fd; /* for inotify, to detect file changes */ +int testing; /* 1 if running test */ +int filter; /* 1 when using as filter (for development) */ +int fifo; /* 1 when reading from fifo (for devel) */ +int draining; /* 1 when draining renamed log file */ + +const char *msg_path = "/var/log/messages"; +const char warn_dump_dir[] = "/var/run/kwarn"; +const char *warn_dump_path = "/var/run/kwarn/warning"; +const char *crash_reporter_command; + +static void Die(const char *format, ...) { + va_list ap; + va_start(ap, format); + fprintf(stderr, "%s: ", prog_name); + vfprintf(stderr, format, ap); + exit(1); +} + +static void RunCrashReporter(void) { + int status = system(crash_reporter_command); + if (status != 0) + Die("%s exited with status %d\n", crash_reporter_command, status); +} + +static uint32_t StringHash(const char *string) { + uint32_t hash = 0; + while (*string != '\0') { + hash = (hash << 5) + hash + *string++; + } + return hash; +} + +/* We expect only a handful of different warnings per boot session, so the + * probability of a collision is very low, and statistically it won't matter + * (unless warnings with the same hash also happens in tandem, which is even + * rarer). + */ +static int HashSeen(uint32_t hash) { + int word_index = (hash & HASH_BITMAP_MASK) / 32; + int bit_index = (hash & HASH_BITMAP_MASK) % 32; + return hash_bitmap[word_index] & 1 << bit_index; +} + +static void SetHashSeen(uint32_t hash) { + int word_index = (hash & HASH_BITMAP_MASK) / 32; + int bit_index = (hash & HASH_BITMAP_MASK) % 32; + hash_bitmap[word_index] |= 1 << bit_index; +} + +int WarnStart(void) { + uint32_t hash; + + if (filter) + return 1; + + hash = StringHash(yytext); + if (!(testing || fifo || filter)) { + CMetricsLibrarySendSparseToUMA(metrics_library, warn_hist_name, (int) hash); + } + if (HashSeen(hash)) + return 0; + SetHashSeen(hash); + + yyout = fopen(warn_dump_path, "w"); + if (yyout == NULL) + Die("fopen %s failed: %s\n", warn_dump_path, strerror(errno)); + fprintf(yyout, "%08x\n", hash); + return 1; +} + +void WarnEnd(void) { + if (filter) + return; + fclose(yyout); + yyout = stdout; /* for debugging */ + RunCrashReporter(); +} + +static void WarnOpenInput(const char *path) { + yyin_fd = open(path, O_RDONLY); + if (yyin_fd < 0) + Die("could not open %s: %s\n", path, strerror(errno)); + if (!fifo) { + /* Set up notification of file growth and rename. */ + i_fd = inotify_init(); + if (i_fd < 0) + Die("inotify_init: %s\n", strerror(errno)); + if (inotify_add_watch(i_fd, path, IN_MODIFY | IN_MOVE_SELF) < 0) + Die("inotify_add_watch: %s\n", strerror(errno)); + } +} + +/* We replace the default YY_INPUT() for the following reasons: + * + * 1. We want to read data as soon as it becomes available, but the default + * YY_INPUT() uses buffered I/O. + * + * 2. We want to block on end of input and wait for the file to grow. + * + * 3. We want to detect log rotation, and reopen the input file as needed. + */ +void WarnInput(char *buf, int *result, size_t max_size) { + while (1) { + *result = read(yyin_fd, buf, max_size); + if (*result < 0) + Die("read: %s", strerror(errno)); + if (*result > 0 || fifo || filter) + return; + if (draining) { + /* Assume we're done with this log, and move to next + * log. Rsyslogd may keep writing to the old log file + * for a while, but we don't care since we don't have + * to be exact. + */ + close(yyin_fd); + if (YYSTATE == WARN) { + /* Be conservative in case we lose the warn + * terminator during the switch---or we may + * collect personally identifiable information. + */ + WarnEnd(); + } + BEGIN(0); /* see above comment */ + sleep(1); /* avoid race with log rotator */ + WarnOpenInput(msg_path); + draining = 0; + continue; + } + /* Nothing left to read, so we must wait. */ + struct inotify_event event; + int n = read(i_fd, &event, sizeof(event)); + if (n <= 0) + Die("inotify: %s\n", strerror(errno)); + if (event.mask & IN_MOVE_SELF) { + /* The file has been renamed. Before switching + * to the new one, we process any remaining + * content of this file. + */ + draining = 1; + } + } +} + +int main(int argc, char **argv) { + int result; + struct passwd *user; + prog_name = argv[0]; + + if (argc == 2 && strcmp(argv[1], "--test") == 0) + testing = 1; + else if (argc == 2 && strcmp(argv[1], "--filter") == 0) + filter = 1; + else if (argc == 2 && strcmp(argv[1], "--fifo") == 0) { + fifo = 1; + } else if (argc != 1) { + fprintf(stderr, + "usage: %s [single-flag]\n" + "flags (for testing only):\n" + "--fifo\tinput is fifo \"fifo\", output is stdout\n" + "--filter\tinput is stdin, output is stdout\n" + "--test\trun self-test\n", + prog_name); + exit(1); + } + + metrics_library = CMetricsLibraryNew(); + CMetricsLibraryInit(metrics_library); + + crash_reporter_command = testing ? + "./warn_collector_test_reporter.sh" : + "/sbin/crash_reporter --kernel_warning"; + + /* When filtering with --filter (for development) use stdin for input. + * Otherwise read input from a file or a fifo. + */ + yyin_fd = fileno(stdin); + if (testing) { + msg_path = "messages"; + warn_dump_path = "warning"; + } + if (fifo) { + msg_path = "fifo"; + } + if (!filter) { + WarnOpenInput(msg_path); + } + + /* Create directory for dump file. Still need to be root here. */ + unlink(warn_dump_path); + if (!testing && !fifo && !filter) { + rmdir(warn_dump_dir); + result = mkdir(warn_dump_dir, 0755); + if (result < 0) + Die("could not create %s: %s\n", + warn_dump_dir, strerror(errno)); + } + + if (0) { + /* TODO(semenzato): put this back in once we decide it's safe + * to make /var/spool/crash rwxrwxrwx root, or use a different + * owner and setuid for the crash reporter as well. + */ + + /* Get low privilege uid, gid. */ + user = getpwnam("chronos"); + if (user == NULL) + Die("getpwnam failed\n"); + + /* Change dump directory ownership. */ + if (chown(warn_dump_dir, user->pw_uid, user->pw_gid) < 0) + Die("chown: %s\n", strerror(errno)); + + /* Drop privileges. */ + if (setuid(user->pw_uid) < 0) { + Die("setuid: %s\n", strerror(errno)); + } + } + + /* Go! */ + return yylex(); +} + +/* Flex should really know not to generate these functions. + */ +void UnusedFunctionWarningSuppressor(void) { + yyunput(0, 0); + (void) input(); +} diff --git a/crash_reporter/warn_collector_test.c b/crash_reporter/warn_collector_test.c new file mode 100644 index 000000000..67f34554e --- /dev/null +++ b/crash_reporter/warn_collector_test.c @@ -0,0 +1,14 @@ +/* Copyright (c) 2013 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +/* + * Test driver for the warn_collector daemon. + */ +#include + +int main(int ac, char **av) +{ + return system("$SRC/warn_collector_test.sh"); +} diff --git a/crash_reporter/warn_collector_test.sh b/crash_reporter/warn_collector_test.sh new file mode 100755 index 000000000..e1f8a2909 --- /dev/null +++ b/crash_reporter/warn_collector_test.sh @@ -0,0 +1,73 @@ +#! /bin/bash +# Copyright (c) 2013 The Chromium OS Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +# Test for warn_collector. Run the warn collector in the background, emulate +# the kernel by appending lines to the log file "messages", and observe the log +# of the (fake) crash reporter each time is run by the warn collector daemon. + +set -e + +SYSROOT=/build/link +LD_LIBRARY_PATH=$SYSROOT/lib64:$SYSROOT/usr/lib64 +PATH=.:$SYSROOT/bin:$SYSROOT/usr/bin:$PATH +TESTLOG=warn-test-log + +trap cleanup EXIT + +cleanup() { + # Kill daemon (if started) on exit + kill % +} + +check_log() { + n_expected=$1 + if [ ! -f $TESTLOG ]; then + echo $TESTLOG was not created + exit 1 + fi + if [ "$(wc -l < $TESTLOG)" -ne $n_expected ]; then + echo expected $n_expected lines in $TESTLOG, found this: + cat $TESTLOG + exit 1 + fi + if egrep -qv '^[0-9a-f]{8}$' $TESTLOG; then + echo found bad lines in $TESTLOG: + cat $TESTLOG + exit 1 + fi +} + +rm -f warn-test-log +cp $SRC/warn_collector_test_reporter.sh . +cp $SRC/TEST_WARNING . +cp TEST_WARNING messages + +# Start the collector daemon. With the --test option, the daemon reads input +# from ./messages, writes the warning into ./warning, and invokes +# ./warn_collector_test_reporter.sh to report the warning. +warn_collector --test & + +# After a while, check that the first warning has been collected. +sleep 1 +check_log 1 + +# Add the same warning to messages, verify that it is NOT collected +cat TEST_WARNING >> messages +sleep 1 +check_log 1 + +# Add a slightly different warning to messages, check that it is collected. +sed s/intel_dp.c/intel_xx.c/ < TEST_WARNING >> messages +sleep 1 +check_log 2 + +# Emulate log rotation, add a warning, and check. +mv messages messages.1 +sed s/intel_dp.c/intel_xy.c/ < TEST_WARNING > messages +sleep 2 +check_log 3 + +# Success! +exit 0 diff --git a/crash_reporter/warn_collector_test_reporter.sh b/crash_reporter/warn_collector_test_reporter.sh new file mode 100755 index 000000000..d8f3fad94 --- /dev/null +++ b/crash_reporter/warn_collector_test_reporter.sh @@ -0,0 +1,16 @@ +#! /bin/sh +# Copyright (c) 2013 The Chromium OS Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +# Replacement for the crash reporter, for testing. Log the first line of the +# "warning" file, which by convention contains the warning hash, and remove the +# file. + +set -e + +exec 1>> warn-test-log +exec 2>> warn-test-log + +head -1 warning +rm warning