diff --git a/bootstat/Android.mk b/bootstat/Android.mk new file mode 100644 index 000000000..348db8851 --- /dev/null +++ b/bootstat/Android.mk @@ -0,0 +1,137 @@ +# +# Copyright (C) 2016 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +LOCAL_PATH := $(call my-dir) + +bootstat_c_includes := external/gtest/include + +bootstat_lib_src_files := \ + boot_event_record_store.cpp \ + event_log_list_builder.cpp + +bootstat_src_files := \ + bootstat.cpp + +bootstat_test_src_files := \ + boot_event_record_store_test.cpp \ + event_log_list_builder_test.cpp \ + testrunner.cpp + +bootstat_shared_libs := \ + libbase \ + liblog + +bootstat_cflags := \ + -Wall \ + -Wextra \ + -Werror + +bootstat_cppflags := \ + -Wno-non-virtual-dtor + +bootstat_debug_cflags := \ + $(bootstat_cflags) \ + -UNDEBUG + +# 524291 corresponds to sysui_histogram, from +# frameworks/base/core/java/com/android/internal/logging/EventLogTags.logtags +bootstat_cflags += -DHISTOGRAM_LOG_TAG=524291 + + +# bootstat static library +# ----------------------------------------------------------------------------- + +include $(CLEAR_VARS) + +LOCAL_MODULE := libbootstat +LOCAL_CFLAGS := $(bootstat_cflags) +LOCAL_CPPFLAGS := $(bootstat_cppflags) +LOCAL_C_INCLUDES := $(bootstat_c_includes) +LOCAL_SHARED_LIBRARIES := $(bootstat_shared_libs) +LOCAL_SRC_FILES := $(bootstat_lib_src_files) + +include $(BUILD_STATIC_LIBRARY) + +# bootstat static library, debug +# ----------------------------------------------------------------------------- + +include $(CLEAR_VARS) + +LOCAL_MODULE := libbootstat_debug +LOCAL_CFLAGS := $(bootstat_cflags) +LOCAL_CPPFLAGS := $(bootstat_debug_cppflags) +LOCAL_C_INCLUDES := $(bootstat_c_includes) +LOCAL_SHARED_LIBRARIES := $(bootstat_shared_libs) +LOCAL_SRC_FILES := $(bootstat_lib_src_files) + +include $(BUILD_STATIC_LIBRARY) + +# bootstat host static library, debug +# ----------------------------------------------------------------------------- + +include $(CLEAR_VARS) + +LOCAL_MODULE := libbootstat_host_debug +LOCAL_CFLAGS := $(bootstat_debug_cflags) +LOCAL_CPPFLAGS := $(bootstat_cppflags) +LOCAL_C_INCLUDES := $(bootstat_c_includes) +LOCAL_SHARED_LIBRARIES := $(bootstat_shared_libs) +LOCAL_SRC_FILES := $(bootstat_lib_src_files) + +include $(BUILD_HOST_STATIC_LIBRARY) + +# bootstat binary +# ----------------------------------------------------------------------------- + +include $(CLEAR_VARS) + +LOCAL_MODULE := bootstat +LOCAL_CFLAGS := $(bootstat_cflags) +LOCAL_CPPFLAGS := $(bootstat_cppflags) +LOCAL_C_INCLUDES := $(bootstat_c_includes) +LOCAL_SHARED_LIBRARIES := $(bootstat_shared_libs) +LOCAL_STATIC_LIBRARIES := libbootstat +LOCAL_SRC_FILES := $(bootstat_src_files) + +include $(BUILD_EXECUTABLE) + +# Native tests +# ----------------------------------------------------------------------------- + +include $(CLEAR_VARS) + +LOCAL_MODULE := bootstat_tests +LOCAL_CFLAGS := $(bootstat_tests_cflags) +LOCAL_CPPFLAGS := $(bootstat_cppflags) +LOCAL_SHARED_LIBRARIES := $(bootstat_shared_libs) +LOCAL_STATIC_LIBRARIES := libbootstat_debug libgmock +LOCAL_SRC_FILES := $(bootstat_test_src_files) + +include $(BUILD_NATIVE_TEST) + +# Host native tests +# ----------------------------------------------------------------------------- + +include $(CLEAR_VARS) + +LOCAL_MODULE := bootstat_tests +LOCAL_CFLAGS := $(bootstat_tests_cflags) +LOCAL_CPPFLAGS := $(bootstat_cppflags) +LOCAL_SHARED_LIBRARIES := $(bootstat_shared_libs) +LOCAL_STATIC_LIBRARIES := libbootstat_host_debug libgmock_host +LOCAL_SRC_FILES := $(bootstat_test_src_files) + +include $(BUILD_HOST_NATIVE_TEST) diff --git a/bootstat/README.md b/bootstat/README.md new file mode 100644 index 000000000..b49495101 --- /dev/null +++ b/bootstat/README.md @@ -0,0 +1,47 @@ +# bootstat # + +The bootstat command records boot events (e.g., `firmware_loaded`, +`boot_complete`) and the relative time at which these events occurred. The +command also aggregates boot event metrics locally and logs the metrics for +analysis. + + Usage: bootstat [options] + options include: + -d Dump the boot event records to the console. + -h Show this help. + -l Log all metrics to logstorage. + -r Record the relative time of a named boot event. + +## Relative time ## + +The timestamp recorded by bootstat is the uptime of the system, i.e., the +number of seconds since the system booted. + +## Recording boot events ## + +To record the relative time of an event during the boot phase, call `bootstat` +with the `-r` option and the name of the boot event. + + $ bootstat -r boot_complete + +The relative time at which the command runs is recorded along with the name of +the boot event to be persisted. + +## Logging boot events ## + +To log the persisted boot events, call `bootstat` with the `-l` option. + + $ bootstat -l + +bootstat logs all boot events recorded using the `-r` option to the EventLog +using the Tron histogram. On GMS devices these logs are uploaded via Clearcut +for aggregation and analysis. + +## Printing boot events ## + +To print the set of persisted boot events, call `bootstat` with the `-p` option. + + $ bootstat -p + Boot events: + ------------ + boot_complete 71 \ No newline at end of file diff --git a/bootstat/boot_event_record_store.cpp b/bootstat/boot_event_record_store.cpp new file mode 100644 index 000000000..0133f5b26 --- /dev/null +++ b/bootstat/boot_event_record_store.cpp @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "boot_event_record_store.h" + +#include +#include +#include +#include +#include +#include +#include + +namespace { + +const char BOOTSTAT_DATA_DIR[] = "/data/misc/bootstat/"; + +// Given a boot even record file at |path|, extracts the event's relative time +// from the record into |uptime|. +bool ParseRecordEventTime(const std::string& path, int32_t* uptime) { + DCHECK_NE(static_cast(nullptr), uptime); + + struct stat file_stat; + if (stat(path.c_str(), &file_stat) == -1) { + PLOG(ERROR) << "Failed to read " << path; + return false; + } + + *uptime = file_stat.st_mtime; + return true; +} + +} // namespace + +BootEventRecordStore::BootEventRecordStore() { + SetStorePath(BOOTSTAT_DATA_DIR); +} + +void BootEventRecordStore::AddBootEvent(const std::string& name) { + std::string uptime_str; + if (!android::base::ReadFileToString("/proc/uptime", &uptime_str)) { + LOG(ERROR) << "Failed to read /proc/uptime"; + } + + std::string record_path = GetBootEventPath(name); + if (creat(record_path.c_str(), S_IRUSR | S_IWUSR) == -1) { + PLOG(ERROR) << "Failed to create " << record_path; + } + + struct stat file_stat; + if (stat(record_path.c_str(), &file_stat) == -1) { + PLOG(ERROR) << "Failed to read " << record_path; + } + + // Cast intentionally rounds down. + time_t uptime = static_cast(strtod(uptime_str.c_str(), NULL)); + struct utimbuf times = {file_stat.st_atime, uptime}; + if (utime(record_path.c_str(), ×) == -1) { + PLOG(ERROR) << "Failed to set mtime for " << record_path; + } +} + +std::vector BootEventRecordStore:: + GetAllBootEvents() const { + std::vector events; + + std::unique_ptr dir(opendir(store_path_.c_str()), closedir); + + // This case could happen due to external manipulation of the filesystem, + // so crash out if the record store doesn't exist. + CHECK_NE(static_cast(nullptr), dir.get()); + + struct dirent* entry; + while ((entry = readdir(dir.get())) != NULL) { + // Only parse regular files. + if (entry->d_type != DT_REG) { + continue; + } + + const std::string event = entry->d_name; + const std::string record_path = GetBootEventPath(event); + int32_t uptime; + if (!ParseRecordEventTime(record_path, &uptime)) { + LOG(ERROR) << "Failed to parse boot time record: " << record_path; + continue; + } + + events.push_back(std::make_pair(event, uptime)); + } + + return events; +} + +void BootEventRecordStore::SetStorePath(const std::string& path) { + DCHECK_EQ('/', path.back()); + store_path_ = path; +} + +std::string BootEventRecordStore::GetBootEventPath( + const std::string& event) const { + DCHECK_EQ('/', store_path_.back()); + return store_path_ + event; +} diff --git a/bootstat/boot_event_record_store.h b/bootstat/boot_event_record_store.h new file mode 100644 index 000000000..efe1e43d1 --- /dev/null +++ b/bootstat/boot_event_record_store.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef BOOT_EVENT_RECORD_STORE_H_ +#define BOOT_EVENT_RECORD_STORE_H_ + +#include +#include +#include +#include +#include +#include + +// BootEventRecordStore manages the persistence of boot events to the record +// store and the retrieval of all boot event records from the store. +class BootEventRecordStore { + public: + // A BootEventRecord consists of the event name and the timestamp the event + // occurred. + typedef std::pair BootEventRecord; + + BootEventRecordStore(); + + // Persists the boot event named |name| in the record store. + void AddBootEvent(const std::string& name); + + // Returns a list of all of the boot events persisted in the record store. + std::vector GetAllBootEvents() const; + + private: + // The tests call SetStorePath to override the default store location with a + // more test-friendly path. + FRIEND_TEST(BootEventRecordStoreTest, AddSingleBootEvent); + FRIEND_TEST(BootEventRecordStoreTest, AddMultipleBootEvents); + + // Sets the filesystem path of the record store. + void SetStorePath(const std::string& path); + + // Constructs the full path of the given boot |event|. + std::string GetBootEventPath(const std::string& event) const; + + // The filesystem path of the record store. + std::string store_path_; + + DISALLOW_COPY_AND_ASSIGN(BootEventRecordStore); +}; + +#endif // BOOT_EVENT_RECORD_STORE_H_ \ No newline at end of file diff --git a/bootstat/boot_event_record_store_test.cpp b/bootstat/boot_event_record_store_test.cpp new file mode 100644 index 000000000..56af0a62e --- /dev/null +++ b/bootstat/boot_event_record_store_test.cpp @@ -0,0 +1,156 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "boot_event_record_store.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using testing::UnorderedElementsAreArray; + +namespace { + +// Returns true if the time difference between |a| and |b| is no larger +// than 10 seconds. This allow for a relatively large fuzz when comparing +// two timestamps taken back-to-back. +bool FuzzUptimeEquals(int32_t a, int32_t b) { + const int32_t FUZZ_SECONDS = 10; + return (abs(a - b) <= FUZZ_SECONDS); +} + +// Returns the uptime as read from /proc/uptime, rounded down to an integer. +int32_t ReadUptime() { + std::string uptime_str; + if (!android::base::ReadFileToString("/proc/uptime", &uptime_str)) { + return -1; + } + + // Cast to int to round down. + return static_cast(strtod(uptime_str.c_str(), NULL)); +} + +// Recursively deletes the directory at |path|. +void DeleteDirectory(const std::string& path) { + typedef std::unique_ptr ScopedDIR; + ScopedDIR dir(opendir(path.c_str()), closedir); + ASSERT_NE(nullptr, dir.get()); + + struct dirent* entry; + while ((entry = readdir(dir.get())) != NULL) { + const std::string entry_name(entry->d_name); + if (entry_name == "." || entry_name == "..") { + continue; + } + + const std::string entry_path = path + "/" + entry_name; + if (entry->d_type == DT_DIR) { + DeleteDirectory(entry_path); + } else { + unlink(entry_path.c_str()); + } + } + + rmdir(path.c_str()); +} + +class BootEventRecordStoreTest : public ::testing::Test { + public: + BootEventRecordStoreTest() { + store_path_ = std::string(store_dir_.path) + "/"; + } + + const std::string& GetStorePathForTesting() const { + return store_path_; + } + + private: + void TearDown() { + // This removes the record store temporary directory even though + // TemporaryDir should already take care of it, but this method cleans up + // the test files added to the directory which prevent TemporaryDir from + // being able to remove the directory. + DeleteDirectory(store_path_); + } + + // A scoped temporary directory. Using this abstraction provides creation of + // the directory and the path to the directory, which is stored in + // |store_path_|. + TemporaryDir store_dir_; + + // The path to the temporary directory used by the BootEventRecordStore to + // persist records. The directory is created and destroyed for each test. + std::string store_path_; + + DISALLOW_COPY_AND_ASSIGN(BootEventRecordStoreTest); +}; + +} // namespace + +TEST_F(BootEventRecordStoreTest, AddSingleBootEvent) { + BootEventRecordStore store; + store.SetStorePath(GetStorePathForTesting()); + + int32_t uptime = ReadUptime(); + ASSERT_NE(-1, uptime); + + store.AddBootEvent("cenozoic"); + + auto events = store.GetAllBootEvents(); + ASSERT_EQ(1U, events.size()); + EXPECT_EQ("cenozoic", events[0].first); + EXPECT_TRUE(FuzzUptimeEquals(uptime, events[0].second)); +} + +TEST_F(BootEventRecordStoreTest, AddMultipleBootEvents) { + BootEventRecordStore store; + store.SetStorePath(GetStorePathForTesting()); + + int32_t uptime = ReadUptime(); + ASSERT_NE(-1, uptime); + + store.AddBootEvent("cretaceous"); + store.AddBootEvent("jurassic"); + store.AddBootEvent("triassic"); + + const std::string EXPECTED_NAMES[] = { + "cretaceous", + "jurassic", + "triassic", + }; + + auto events = store.GetAllBootEvents(); + ASSERT_EQ(3U, events.size()); + + std::vector names; + std::vector timestamps; + for (auto i = events.begin(); i != events.end(); ++i) { + names.push_back(i->first); + timestamps.push_back(i->second); + } + + EXPECT_THAT(names, UnorderedElementsAreArray(EXPECTED_NAMES)); + + for (auto i = timestamps.cbegin(); i != timestamps.cend(); ++i) { + EXPECT_TRUE(FuzzUptimeEquals(uptime, *i)); + } +} diff --git a/bootstat/bootstat.cpp b/bootstat/bootstat.cpp new file mode 100644 index 000000000..a4cbf4e83 --- /dev/null +++ b/bootstat/bootstat.cpp @@ -0,0 +1,144 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// The bootstat command provides options to persist boot events with the current +// timestamp, dump the persisted events, and log all events to EventLog to be +// uploaded to Android log storage via Tron. + +//#define LOG_TAG "bootstat" + +#include +#include +#include +#include +#include +#include +#include +#include "boot_event_record_store.h" +#include "event_log_list_builder.h" + +namespace { + +// Builds an EventLog buffer named |event| containing |data| and writes +// the log into the Tron histogram logs. +void LogBootEvent(const std::string& event, int32_t data) { + LOG(INFO) << "Logging boot time: " << event << " " << data; + + EventLogListBuilder log_builder; + log_builder.Append(event); + log_builder.Append(data); + + std::unique_ptr log; + size_t size; + log_builder.Release(&log, &size); + + android_bWriteLog(HISTOGRAM_LOG_TAG, log.get(), size); +} + +// Scans the boot event record store for record files and logs each boot event +// via EventLog. +void LogBootEvents() { + BootEventRecordStore boot_event_store; + + auto events = boot_event_store.GetAllBootEvents(); + for (auto i = events.cbegin(); i != events.cend(); ++i) { + LogBootEvent(i->first, i->second); + } +} + +void PrintBootEvents() { + printf("Boot events:\n"); + printf("------------\n"); + + BootEventRecordStore boot_event_store; + auto events = boot_event_store.GetAllBootEvents(); + for (auto i = events.cbegin(); i != events.cend(); ++i) { + printf("%s\t%d\n", i->first.c_str(), i->second); + } +} + +void ShowHelp(const char *cmd) { + fprintf(stderr, "Usage: %s [options]\n", cmd); + fprintf(stderr, + "options include:\n" + " -d Dump the boot event records to the console.\n" + " -h Show this help.\n" + " -l Log all metrics to logstorage.\n" + " -r Record the timestamp of a named boot event.\n"); +} + +// Constructs a readable, printable string from the givencommand line +// arguments. +std::string GetCommandLine(int argc, char **argv) { + std::string cmd; + for (int i = 0; i < argc; ++i) { + cmd += argv[i]; + cmd += " "; + } + + return cmd; +} + +} // namespace + +int main(int argc, char **argv) { + android::base::InitLogging(argv); + + const std::string cmd_line = GetCommandLine(argc, argv); + LOG(INFO) << "Service started: " << cmd_line; + + int opt = 0; + while ((opt = getopt(argc, argv, "hlpr:")) != -1) { + switch (opt) { + case 'h': { + ShowHelp(argv[0]); + break; + } + + case 'l': { + LogBootEvents(); + break; + } + + case 'p': { + PrintBootEvents(); + break; + } + + case 'r': { + // |optarg| is an external variable set by getopt representing + // the option argument. + const char* event = optarg; + + BootEventRecordStore boot_event_store; + boot_event_store.AddBootEvent(event); + break; + } + + default: { + DCHECK_EQ(opt, '?'); + + // |optopt| is an external variable set by getopt representing + // the value of the invalid option. + LOG(ERROR) << "Invalid option: " << optopt; + ShowHelp(argv[0]); + return EXIT_FAILURE; + } + } + } + + return 0; +} diff --git a/bootstat/event_log_list_builder.cpp b/bootstat/event_log_list_builder.cpp new file mode 100644 index 000000000..017a7c529 --- /dev/null +++ b/bootstat/event_log_list_builder.cpp @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "event_log_list_builder.h" + +#include +#include +#include +#include + +namespace { + +const size_t MAX_EVENT_PAYLOAD_SIZE = 512 - 1; // Leave room for final '\n'. +const size_t EVENT_TYPE_SIZE = 1; // Size in bytes of the event type marker. + +} // namespace + +EventLogListBuilder::EventLogListBuilder() + : payload_count_(0), + payload_size_(0), + payload_(std::make_unique(MAX_EVENT_PAYLOAD_SIZE)) { + memset(payload_.get(), 0, MAX_EVENT_PAYLOAD_SIZE); + + // Set up the top-level EventLog data type. + AppendByte(EVENT_TYPE_LIST); + + // Skip over the byte prepresenting the number of items in the list. This + // value is set in Release(). + payload_size_++; +} + +bool EventLogListBuilder::Append(int value) { + DCHECK_NE(static_cast(nullptr), payload_.get()); + + if (!IsSpaceAvailable(sizeof(value) + EVENT_TYPE_SIZE)) { + return false; + } + + AppendByte(EVENT_TYPE_INT); + AppendData(&value, sizeof(value)); + + payload_count_++; + return true; +} + +bool EventLogListBuilder::Append(const std::string& value) { + DCHECK_NE(static_cast(nullptr), payload_.get()); + + int len = value.length(); + if (!IsSpaceAvailable(sizeof(len) + len)) { + return false; + } + + AppendByte(EVENT_TYPE_STRING); + AppendData(&len, sizeof(len)); + AppendData(value.c_str(), len); + + payload_count_++; + return true; +} + +void EventLogListBuilder::Release(std::unique_ptr* log, + size_t* size) { + // Finalize the log payload. + payload_[1] = payload_count_; + + // Return the log payload. + *size = payload_size_; + *log = std::move(payload_); +} + +void EventLogListBuilder::AppendData(const void* data, size_t size) { + DCHECK_LT(payload_size_ + size, MAX_EVENT_PAYLOAD_SIZE); + memcpy(&payload_[payload_size_], data, size); + payload_size_ += size; +} + +void EventLogListBuilder::AppendByte(uint8_t byte) { + DCHECK_LT(payload_size_ + sizeof(byte), MAX_EVENT_PAYLOAD_SIZE); + payload_[payload_size_++] = byte; +} + +bool EventLogListBuilder::IsSpaceAvailable(size_t value_size) { + size_t space_needed = value_size + EVENT_TYPE_SIZE; + if (payload_size_ + space_needed > MAX_EVENT_PAYLOAD_SIZE) { + size_t remaining = MAX_EVENT_PAYLOAD_SIZE - payload_size_; + LOG(WARNING) << "Not enough space for value. remain=" << + remaining << "; needed=" << space_needed; + return false; + } + + return true; +} diff --git a/bootstat/event_log_list_builder.h b/bootstat/event_log_list_builder.h new file mode 100644 index 000000000..52d623f4d --- /dev/null +++ b/bootstat/event_log_list_builder.h @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef EVENT_LOG_LIST_BUILDER_H_ +#define EVENT_LOG_LIST_BUILDER_H_ + +#include +#include + +#include + +// EventLogListBuilder provides a mechanism to build an EventLog list +// consisting of int and string EventLog values. +// +// NOTE: This class does not provide the ability to append an embedded list, +// i.e., a list containing a list. +class EventLogListBuilder { + public: + EventLogListBuilder(); + + // Append a single value of a specified type. + bool Append(int value); + bool Append(const std::string& value); + + // Finalizes construction of the EventLog list and releases the data + // to the caller. Caller takes ownership of the payload. No further calls + // to append* may be made once the payload is acquired by the caller. + void Release(std::unique_ptr* log, size_t* size); + + private: + // Appends |data| of the given |size| to the payload. + void AppendData(const void* data, size_t size); + + // Appends a single byte to the payload. + void AppendByte(uint8_t byte); + + // Returns true iff the remaining capacity in |payload_| is large enough to + // accommodate |value_size| bytes. The space required to log the event type + // is included in the internal calculation so must not be passed in to + // |value_size|. + bool IsSpaceAvailable(size_t value_size); + + // The number of items in the EventLog list. + size_t payload_count_; + + // The size of the data stored in |payload_|. Used to track where to insert + // new data. + size_t payload_size_; + + // The payload constructed by calls to log*. The payload may only contain + // MAX_EVENT_PAYLOAD (512) bytes. + std::unique_ptr payload_; + + DISALLOW_COPY_AND_ASSIGN(EventLogListBuilder); +}; + + #endif // EVENT_LOG_LIST_BUILDER_H_ diff --git a/bootstat/event_log_list_builder_test.cpp b/bootstat/event_log_list_builder_test.cpp new file mode 100644 index 000000000..affb4bf13 --- /dev/null +++ b/bootstat/event_log_list_builder_test.cpp @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "event_log_list_builder.h" + +#include +#include +#include +#include + +using testing::ElementsAreArray; + +TEST(EventLogListBuilder, Empty) { + EventLogListBuilder builder; + + const uint8_t EXPECTED_LOG[] = { + EVENT_TYPE_LIST, + 0, // Number of items in the list. + }; + + std::unique_ptr log; + size_t size; + builder.Release(&log, &size); + EXPECT_EQ(2U, size); + + uint8_t* log_data = log.get(); + EXPECT_THAT(std::vector(log_data, log_data + size), + ElementsAreArray(EXPECTED_LOG)); +} + +TEST(EventLogListBuilder, SingleInt) { + EventLogListBuilder builder; + + const uint8_t EXPECTED_LOG[] = { + EVENT_TYPE_LIST, + 1, // Number of items in the list. + EVENT_TYPE_INT, + 42, 0, 0, 0, // 4 byte integer value. + }; + + builder.Append(42); + + std::unique_ptr log; + size_t size; + builder.Release(&log, &size); + EXPECT_EQ(7U, size); + + uint8_t* log_data = log.get(); + EXPECT_THAT(std::vector(log_data, log_data + size), + ElementsAreArray(EXPECTED_LOG)); +} + +TEST(EventLogListBuilder, SingleString) { + EventLogListBuilder builder; + + const uint8_t EXPECTED_LOG[] = { + EVENT_TYPE_LIST, + 1, // Number of items in the list. + EVENT_TYPE_STRING, + 5, 0, 0, 0, // 4 byte length of the string. + 'D', 'r', 'o', 'i', 'd', + }; + + builder.Append("Droid"); + + std::unique_ptr log; + size_t size; + builder.Release(&log, &size); + EXPECT_EQ(12U, size); + + uint8_t* log_data = log.get(); + EXPECT_THAT(std::vector(log_data, log_data + size), + ElementsAreArray(EXPECTED_LOG)); +} + +TEST(EventLogListBuilder, IntThenString) { + EventLogListBuilder builder; + + const uint8_t EXPECTED_LOG[] = { + EVENT_TYPE_LIST, + 2, // Number of items in the list. + EVENT_TYPE_INT, + 42, 0, 0, 0, // 4 byte integer value. + EVENT_TYPE_STRING, + 5, 0, 0, 0, // 4 byte length of the string. + 'D', 'r', 'o', 'i', 'd', + }; + + builder.Append(42); + builder.Append("Droid"); + + std::unique_ptr log; + size_t size; + builder.Release(&log, &size); + EXPECT_EQ(17U, size); + + uint8_t* log_data = log.get(); + EXPECT_THAT(std::vector(log_data, log_data + size), + ElementsAreArray(EXPECTED_LOG)); +} diff --git a/bootstat/testrunner.cpp b/bootstat/testrunner.cpp new file mode 100644 index 000000000..ff8611ede --- /dev/null +++ b/bootstat/testrunner.cpp @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +int main(int argc, char** argv) { + ::testing::InitGoogleTest(&argc, argv); + android::base::InitLogging(argv, android::base::StderrLogger); + return RUN_ALL_TESTS(); +}