Merge "system/core: Add initial implementation of the bootstat command."
This commit is contained in:
commit
d0009911de
10 changed files with 974 additions and 0 deletions
137
bootstat/Android.mk
Normal file
137
bootstat/Android.mk
Normal file
|
@ -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)
|
47
bootstat/README.md
Normal file
47
bootstat/README.md
Normal file
|
@ -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
|
116
bootstat/boot_event_record_store.cpp
Normal file
116
bootstat/boot_event_record_store.cpp
Normal file
|
@ -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 <dirent.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <utime.h>
|
||||
#include <cstdlib>
|
||||
#include <base/file.h>
|
||||
#include <base/logging.h>
|
||||
|
||||
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<int32_t*>(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<time_t>(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::BootEventRecord> BootEventRecordStore::
|
||||
GetAllBootEvents() const {
|
||||
std::vector<BootEventRecord> events;
|
||||
|
||||
std::unique_ptr<DIR, decltype(&closedir)> 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<DIR*>(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;
|
||||
}
|
61
bootstat/boot_event_record_store.h
Normal file
61
bootstat/boot_event_record_store.h
Normal file
|
@ -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 <cstdint>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <base/macros.h>
|
||||
#include <gtest/gtest_prod.h>
|
||||
|
||||
// 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<std::string, int32_t> 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<BootEventRecord> 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_
|
156
bootstat/boot_event_record_store_test.cpp
Normal file
156
bootstat/boot_event_record_store_test.cpp
Normal file
|
@ -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 <dirent.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <base/file.h>
|
||||
#include <base/test_utils.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include <gmock/gmock.h>
|
||||
|
||||
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<int32_t>(strtod(uptime_str.c_str(), NULL));
|
||||
}
|
||||
|
||||
// Recursively deletes the directory at |path|.
|
||||
void DeleteDirectory(const std::string& path) {
|
||||
typedef std::unique_ptr<DIR, decltype(&closedir)> 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<std::string> names;
|
||||
std::vector<int32_t> 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));
|
||||
}
|
||||
}
|
144
bootstat/bootstat.cpp
Normal file
144
bootstat/bootstat.cpp
Normal file
|
@ -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 <unistd.h>
|
||||
#include <cstddef>
|
||||
#include <cstdio>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <base/logging.h>
|
||||
#include <log/log.h>
|
||||
#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<uint8_t[]> 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;
|
||||
}
|
106
bootstat/event_log_list_builder.cpp
Normal file
106
bootstat/event_log_list_builder.cpp
Normal file
|
@ -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 <cinttypes>
|
||||
#include <string>
|
||||
#include <base/logging.h>
|
||||
#include <log/log.h>
|
||||
|
||||
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<uint8_t[]>(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<uint8_t*>(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<uint8_t*>(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<uint8_t[]>* 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;
|
||||
}
|
70
bootstat/event_log_list_builder.h
Normal file
70
bootstat/event_log_list_builder.h
Normal file
|
@ -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 <cstdint>
|
||||
#include <memory>
|
||||
|
||||
#include <base/macros.h>
|
||||
|
||||
// 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<uint8_t[]>* 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<uint8_t[]> payload_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(EventLogListBuilder);
|
||||
};
|
||||
|
||||
#endif // EVENT_LOG_LIST_BUILDER_H_
|
113
bootstat/event_log_list_builder_test.cpp
Normal file
113
bootstat/event_log_list_builder_test.cpp
Normal file
|
@ -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 <inttypes.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include <gmock/gmock.h>
|
||||
#include <log/log.h>
|
||||
|
||||
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<uint8_t[]> log;
|
||||
size_t size;
|
||||
builder.Release(&log, &size);
|
||||
EXPECT_EQ(2U, size);
|
||||
|
||||
uint8_t* log_data = log.get();
|
||||
EXPECT_THAT(std::vector<uint8_t>(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<uint8_t[]> log;
|
||||
size_t size;
|
||||
builder.Release(&log, &size);
|
||||
EXPECT_EQ(7U, size);
|
||||
|
||||
uint8_t* log_data = log.get();
|
||||
EXPECT_THAT(std::vector<uint8_t>(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<uint8_t[]> log;
|
||||
size_t size;
|
||||
builder.Release(&log, &size);
|
||||
EXPECT_EQ(12U, size);
|
||||
|
||||
uint8_t* log_data = log.get();
|
||||
EXPECT_THAT(std::vector<uint8_t>(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<uint8_t[]> log;
|
||||
size_t size;
|
||||
builder.Release(&log, &size);
|
||||
EXPECT_EQ(17U, size);
|
||||
|
||||
uint8_t* log_data = log.get();
|
||||
EXPECT_THAT(std::vector<uint8_t>(log_data, log_data + size),
|
||||
ElementsAreArray(EXPECTED_LOG));
|
||||
}
|
24
bootstat/testrunner.cpp
Normal file
24
bootstat/testrunner.cpp
Normal file
|
@ -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 <base/logging.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
android::base::InitLogging(argv, android::base::StderrLogger);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
Loading…
Reference in a new issue