Merge "add support for thread-local objects with destructors"
diff --git a/aos/build/aos_all.gyp b/aos/build/aos_all.gyp
index bedaed2..2f6be93 100644
--- a/aos/build/aos_all.gyp
+++ b/aos/build/aos_all.gyp
@@ -21,6 +21,7 @@
'<(AOS)/common/common.gyp:queue_test',
'<(AOS)/common/common.gyp:die_test',
'<(AOS)/common/common.gyp:queue_types_test',
+ '<(AOS)/common/util/util.gyp:string_to_num_test',
'<(AOS)/common/util/util.gyp:trapezoid_profile_test',
'<(AOS)/common/util/util.gyp:wrapping_counter_test',
'<(AOS)/common/libc/libc.gyp:dirname_test',
diff --git a/aos/common/util/string_to_num.h b/aos/common/util/string_to_num.h
new file mode 100644
index 0000000..c6dcb30
--- /dev/null
+++ b/aos/common/util/string_to_num.h
@@ -0,0 +1,29 @@
+#ifndef AOS_COMMON_UTIL_STRING_TO_NUM_H_
+#define AOS_COMMON_UTIL_STRING_TO_NUM_H_
+
+#include <sstream>
+#include <string>
+
+namespace aos {
+namespace util {
+
+// Converts a string into a specified integral type. If it can't be converted
+// completely or at all, or if the converted number would overflow the
+// specified integral type, it returns false.
+template<typename T>
+inline bool StringToInteger(const ::std::string &input, T *out_num) {
+ ::std::istringstream stream(input);
+ stream >> *out_num;
+
+ if (stream.fail() || !stream.eof()) {
+ return false;
+ }
+
+ return true;
+}
+
+
+} // util
+} // aos
+
+#endif
diff --git a/aos/common/util/string_to_num_test.cc b/aos/common/util/string_to_num_test.cc
new file mode 100644
index 0000000..e1ee1cb
--- /dev/null
+++ b/aos/common/util/string_to_num_test.cc
@@ -0,0 +1,43 @@
+#include <stdint.h>
+
+#include <string>
+
+#include "gtest/gtest.h"
+
+#include "aos/common/util/string_to_num.h"
+
+namespace aos {
+namespace util {
+namespace testing {
+
+TEST(StringToNumTest, CorrectNumber) {
+ int result;
+ ASSERT_TRUE(StringToInteger<int>(::std::string("42"), &result));
+ EXPECT_EQ(result, 42);
+}
+
+TEST(StringToNumTest, NegativeTest) {
+ int result;
+ ASSERT_TRUE(StringToInteger<int>(::std::string("-42"), &result));
+ EXPECT_EQ(result, -42);
+}
+
+TEST(StringToNumTest, NonNumber) {
+ int result;
+ ASSERT_FALSE(StringToInteger<int>(::std::string("Daniel"), &result));
+}
+
+TEST(StringToNumTest, NumberWithText) {
+ int result;
+ ASSERT_FALSE(StringToInteger<int>(::std::string("42Daniel"), &result));
+}
+
+TEST(StringToNumTest, OverflowTest) {
+ uint32_t result;
+ // 2 << 32 should overflow.
+ ASSERT_FALSE(StringToInteger<uint32_t>(::std::string("4294967296"), &result));
+}
+
+} // testing
+} // util
+} // aos
diff --git a/aos/common/util/util.gyp b/aos/common/util/util.gyp
index fa1fb7f..5008dd8 100644
--- a/aos/common/util/util.gyp
+++ b/aos/common/util/util.gyp
@@ -70,6 +70,24 @@
],
},
{
+ 'target_name': 'string_to_num',
+ 'type': 'static_library',
+ 'sources': [
+ #'string_to_num.h',
+ ],
+ },
+ {
+ 'target_name': 'string_to_num_test',
+ 'type': 'executable',
+ 'sources': [
+ 'string_to_num_test.cc',
+ ],
+ 'dependencies': [
+ ':string_to_num',
+ '<(EXTERNALS):gtest',
+ ],
+ },
+ {
'target_name': 'thread',
'type': 'static_library',
'sources': [
diff --git a/aos/linux_code/logging/log_displayer.cc b/aos/linux_code/logging/log_displayer.cc
index 805267c..bc00ddc 100644
--- a/aos/linux_code/logging/log_displayer.cc
+++ b/aos/linux_code/logging/log_displayer.cc
@@ -7,11 +7,14 @@
#include <inttypes.h>
#include <algorithm>
+#include <memory>
+#include <string>
#include "aos/linux_code/logging/binary_log_file.h"
#include "aos/common/queue_types.h"
#include "aos/common/logging/logging_impl.h"
#include "aos/common/logging/logging_printf_formats.h"
+#include "aos/common/util/string_to_num.h"
using ::aos::logging::linux_code::LogFileMessageHeader;
@@ -24,24 +27,70 @@
" -n, --name NAME only display entries from processes named NAME\n"
" -l, --level LEVEL "
"only display log entries at least as important as LEVEL\n"
- " // -p, --pid PID only display log entries from process PID\n"
+ " -p, --pid PID only display log entries from process PID\n"
" -f, --follow "
"wait when the end of the file is reached (implies --end)\n"
" -t, --terminate stop when the end of file is reached (default)\n"
" -b, --beginning start at the beginning of the file (default)\n"
" -e, --end start at the end of the file\n"
" -s, --skip NUMBER skip NUMBER matching logs\n"
- " // -m, --max NUMBER only display up to NUMBER logs\n"
+ " -m, --max NUMBER only display up to NUMBER logs\n"
" // -o, --format FORMAT use FORMAT to display log entries\n"
" -h, --help display this help and exit\n"
"\n"
"LEVEL must be DEBUG, INFO, WARNING, ERROR, or FATAL.\n"
" It defaults to INFO.\n"
"\n"
- "TODO(brians) implement the commented out ones and changing FILE\n";
+ "TODO(brians) implement the commented out ones.\n";
+
+const char *kExampleUsages = "To view logs from the shooter:\n"
+ "\t`log_displayer -n shooter`\n"
+ "To view debug logs from the shooter:\n"
+ "\t`log_displayer -n shooter -l DEBUG`\n"
+ "To view what the shooter is logging in realtime:\n"
+ "\t`log_displayer -f -n shooter`\n"
+ "To view shooter logs from an old log file:\n"
+ "\t`log_displayer aos_log-<number> -n shooter`\n"
+ "To view the statuses of the shooter hall effects in realtime:\n"
+ "\t`log_displayer -f -n shooter -l DEBUG | grep .Position`\n";
void PrintHelpAndExit() {
fprintf(stderr, "Usage: %s %s", program_invocation_name, kArgsHelp);
+ fprintf(stderr, "\nExample usages:\n\n%s", kExampleUsages);
+
+ // Get the possible executables from start_list.txt.
+ FILE *start_list = fopen("start_list.txt", "r");
+ if (start_list == NULL) {
+ PLOG(FATAL, "Unable to open start_list.txt");
+ }
+
+ // Get file size.
+ if (fseek(start_list, 0, SEEK_END)) {
+ PLOG(FATAL, "fseek() failed while reading start_list.txt");
+ }
+ int size = ftell(start_list);
+ if (size < 0) {
+ PLOG(FATAL, "ftell() failed while reading start_list.txt");
+ }
+ rewind(start_list);
+
+ ::std::unique_ptr<char[]> contents(new char[size + 1]);
+ if (contents == NULL) {
+ LOG(FATAL, "malloc() failed while reading start_list.txt.\n");
+ }
+ size_t bytes_read = fread(contents.get(), 1, size, start_list);
+ if (bytes_read < static_cast<size_t>(size)) {
+ LOG(FATAL, "Read %zu bytes from start_list.txt, expected %d.\n",
+ bytes_read, size);
+ }
+
+ // printf doesn't like strings without the \0.
+ contents[size] = '\0';
+ fprintf(stderr, "\nPossible arguments for the -n option:\n%s", contents.get());
+
+ if (fclose(start_list)) {
+ LOG(FATAL, "fclose() failed.\n");
+ }
exit(EXIT_SUCCESS);
}
@@ -56,6 +105,8 @@
// Whether we need to skip everything until we get to the end of the file.
bool skip_to_end = false;
const char *filename = "aos_log-current";
+ int display_max = 0;
+ int32_t source_pid = -1;
::aos::logging::Init();
::aos::logging::AddImplementation(
@@ -88,7 +139,7 @@
}
switch (c) {
case 0:
- fprintf(stderr, "LogDisplayer: got a 0 option but didn't set up any\n");
+ fputs("LogDisplayer: got a 0 option but didn't set up any\n", stderr);
abort();
case 'n':
filter_name = optarg;
@@ -102,7 +153,14 @@
}
break;
case 'p':
- abort();
+ if (!::aos::util::StringToInteger(::std::string(optarg), &source_pid)) {
+ fprintf(stderr, "ERROR: -p expects a number, not '%s'.\n", optarg);
+ exit(EXIT_FAILURE);
+ }
+ if (source_pid < 0) {
+ fprintf(stderr, "LogDisplayer: invalid pid '%s'\n", optarg);
+ exit(EXIT_FAILURE);
+ }
break;
case 'f':
follow = true;
@@ -118,7 +176,15 @@
skip_to_end = true;
break;
case 'm':
- abort();
+ if (!::aos::util::StringToInteger(::std::string(optarg), &display_max)) {
+ fprintf(stderr, "ERROR: -m expects a number, not '%s'.\n", optarg);
+ exit(EXIT_FAILURE);
+ }
+ if (display_max <= 0) {
+ fprintf(stderr, "LogDisplayer: invalid max log number '%s'\n",
+ optarg);
+ exit(EXIT_FAILURE);
+ }
break;
case 'o':
abort();
@@ -135,16 +201,22 @@
}
}
- fprintf(stderr, "displaying down to level %s from file '%s'\n",
- ::aos::logging::log_str(filter_level), filename);
if (optind < argc) {
- fprintf(stderr, "non-option ARGV-elements: ");
+ // We got a filename.
+ filename = argv[optind++];
+ }
+ if (optind < argc) {
+ fputs("non-option ARGV-elements: ", stderr);
while (optind < argc) {
fprintf(stderr, "%s\n", argv[optind++]);
}
}
+ fprintf(stderr, "displaying down to level %s from file '%s'\n",
+ ::aos::logging::log_str(filter_level), filename);
+
int fd = open(filename, O_RDONLY);
+
if (fd == -1) {
PLOG(FATAL, "couldn't open file '%s' for reading", filename);
}
@@ -155,6 +227,7 @@
}
const LogFileMessageHeader *msg;
+ int displayed = 0;
do {
msg = reader.ReadNextMessage(follow);
if (msg == NULL) {
@@ -162,6 +235,11 @@
return 0;
}
+ if (source_pid >= 0 && msg->source != source_pid) {
+ // Message is from the wrong process.
+ continue;
+ }
+
if (msg->type == LogFileMessageHeader::MessageType::kStructType) {
size_t bytes = msg->message_size;
::aos::MessageType *type = ::aos::MessageType::Deserialize(
@@ -190,6 +268,11 @@
}
}
+ if (display_max && displayed++ >= display_max) {
+ fputs("Not displaying the rest of the messages.\n", stderr);
+ return 0;
+ }
+
const char *position =
reinterpret_cast<const char *>(msg + 1) + msg->name_size;
#define BASE_ARGS \