1/* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17#include "boot_event_record_store.h" 18 19#include <dirent.h> 20#include <fcntl.h> 21#include <sys/stat.h> 22#include <utime.h> 23#include <cstdlib> 24#include <string> 25#include <utility> 26#include <android-base/file.h> 27#include <android-base/logging.h> 28#include <android-base/parseint.h> 29#include "histogram_logger.h" 30#include "uptime_parser.h" 31 32namespace { 33 34const char BOOTSTAT_DATA_DIR[] = "/data/misc/bootstat/"; 35 36// Given a boot even record file at |path|, extracts the event's relative time 37// from the record into |uptime|. 38bool ParseRecordEventTime(const std::string& path, int32_t* uptime) { 39 DCHECK_NE(static_cast<int32_t*>(nullptr), uptime); 40 41 struct stat file_stat; 42 if (stat(path.c_str(), &file_stat) == -1) { 43 PLOG(ERROR) << "Failed to read " << path; 44 return false; 45 } 46 47 *uptime = file_stat.st_mtime; 48 49 // The following code (till function exit) is a debug test to ensure the 50 // validity of the file mtime value, i.e., to check that the record file 51 // mtime values are not changed once set. 52 // TODO(jhawkins): Remove this code. 53 std::string content; 54 if (!android::base::ReadFileToString(path, &content)) { 55 PLOG(ERROR) << "Failed to read " << path; 56 return false; 57 } 58 59 // Ignore existing bootstat records (which do not contain file content). 60 if (!content.empty()) { 61 int32_t value; 62 if (android::base::ParseInt(content.c_str(), &value)) { 63 bootstat::LogHistogram("bootstat_mtime_matches_content", value == *uptime); 64 } 65 } 66 67 return true; 68} 69 70} // namespace 71 72BootEventRecordStore::BootEventRecordStore() { 73 SetStorePath(BOOTSTAT_DATA_DIR); 74} 75 76void BootEventRecordStore::AddBootEvent(const std::string& event) { 77 AddBootEventWithValue(event, bootstat::ParseUptime()); 78} 79 80// The implementation of AddBootEventValue makes use of the mtime file 81// attribute to store the value associated with a boot event in order to 82// optimize on-disk size requirements and small-file thrashing. 83void BootEventRecordStore::AddBootEventWithValue( 84 const std::string& event, int32_t value) { 85 std::string record_path = GetBootEventPath(event); 86 int record_fd = creat(record_path.c_str(), S_IRUSR | S_IWUSR); 87 if (record_fd == -1) { 88 PLOG(ERROR) << "Failed to create " << record_path; 89 return; 90 } 91 92 // Writing the value as content in the record file is a debug measure to 93 // ensure the validity of the file mtime value, i.e., to check that the record 94 // file mtime values are not changed once set. 95 // TODO(jhawkins): Remove this block. 96 if (!android::base::WriteStringToFd(std::to_string(value), record_fd)) { 97 PLOG(ERROR) << "Failed to write value to " << record_path; 98 close(record_fd); 99 return; 100 } 101 102 // Fill out the stat structure for |record_path| in order to get the atime to 103 // set in the utime() call. 104 struct stat file_stat; 105 if (stat(record_path.c_str(), &file_stat) == -1) { 106 PLOG(ERROR) << "Failed to read " << record_path; 107 close(record_fd); 108 return; 109 } 110 111 // Set the |modtime| of the file to store the value of the boot event while 112 // preserving the |actime| (as read by stat). 113 struct utimbuf times = {/* actime */ file_stat.st_atime, /* modtime */ value}; 114 if (utime(record_path.c_str(), ×) == -1) { 115 PLOG(ERROR) << "Failed to set mtime for " << record_path; 116 close(record_fd); 117 return; 118 } 119 120 close(record_fd); 121} 122 123bool BootEventRecordStore::GetBootEvent( 124 const std::string& event, BootEventRecord* record) const { 125 CHECK_NE(static_cast<BootEventRecord*>(nullptr), record); 126 CHECK(!event.empty()); 127 128 const std::string record_path = GetBootEventPath(event); 129 int32_t uptime; 130 if (!ParseRecordEventTime(record_path, &uptime)) { 131 LOG(ERROR) << "Failed to parse boot time record: " << record_path; 132 return false; 133 } 134 135 *record = std::make_pair(event, uptime); 136 return true; 137} 138 139std::vector<BootEventRecordStore::BootEventRecord> BootEventRecordStore:: 140 GetAllBootEvents() const { 141 std::vector<BootEventRecord> events; 142 143 std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(store_path_.c_str()), closedir); 144 145 // This case could happen due to external manipulation of the filesystem, 146 // so crash out if the record store doesn't exist. 147 CHECK_NE(static_cast<DIR*>(nullptr), dir.get()); 148 149 struct dirent* entry; 150 while ((entry = readdir(dir.get())) != NULL) { 151 // Only parse regular files. 152 if (entry->d_type != DT_REG) { 153 continue; 154 } 155 156 const std::string event = entry->d_name; 157 BootEventRecord record; 158 if (!GetBootEvent(event, &record)) { 159 LOG(ERROR) << "Failed to parse boot time event: " << event; 160 continue; 161 } 162 163 events.push_back(record); 164 } 165 166 return events; 167} 168 169void BootEventRecordStore::SetStorePath(const std::string& path) { 170 DCHECK_EQ('/', path.back()); 171 store_path_ = path; 172} 173 174std::string BootEventRecordStore::GetBootEventPath( 175 const std::string& event) const { 176 DCHECK_EQ('/', store_path_.back()); 177 return store_path_ + event; 178} 179