1abd73e617970e2e4cb390d5f66cfd0dda57579d8James Hawkins/* 2abd73e617970e2e4cb390d5f66cfd0dda57579d8James Hawkins * Copyright (C) 2016 The Android Open Source Project 3abd73e617970e2e4cb390d5f66cfd0dda57579d8James Hawkins * 4abd73e617970e2e4cb390d5f66cfd0dda57579d8James Hawkins * Licensed under the Apache License, Version 2.0 (the "License"); 5abd73e617970e2e4cb390d5f66cfd0dda57579d8James Hawkins * you may not use this file except in compliance with the License. 6abd73e617970e2e4cb390d5f66cfd0dda57579d8James Hawkins * You may obtain a copy of the License at 7abd73e617970e2e4cb390d5f66cfd0dda57579d8James Hawkins * 8abd73e617970e2e4cb390d5f66cfd0dda57579d8James Hawkins * http://www.apache.org/licenses/LICENSE-2.0 9abd73e617970e2e4cb390d5f66cfd0dda57579d8James Hawkins * 10abd73e617970e2e4cb390d5f66cfd0dda57579d8James Hawkins * Unless required by applicable law or agreed to in writing, software 11abd73e617970e2e4cb390d5f66cfd0dda57579d8James Hawkins * distributed under the License is distributed on an "AS IS" BASIS, 12abd73e617970e2e4cb390d5f66cfd0dda57579d8James Hawkins * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13abd73e617970e2e4cb390d5f66cfd0dda57579d8James Hawkins * See the License for the specific language governing permissions and 14abd73e617970e2e4cb390d5f66cfd0dda57579d8James Hawkins * limitations under the License. 15abd73e617970e2e4cb390d5f66cfd0dda57579d8James Hawkins */ 16abd73e617970e2e4cb390d5f66cfd0dda57579d8James Hawkins 17abd73e617970e2e4cb390d5f66cfd0dda57579d8James Hawkins#include "boot_event_record_store.h" 18abd73e617970e2e4cb390d5f66cfd0dda57579d8James Hawkins 19abd73e617970e2e4cb390d5f66cfd0dda57579d8James Hawkins#include <dirent.h> 20abd73e617970e2e4cb390d5f66cfd0dda57579d8James Hawkins#include <fcntl.h> 21abd73e617970e2e4cb390d5f66cfd0dda57579d8James Hawkins#include <sys/stat.h> 22abd73e617970e2e4cb390d5f66cfd0dda57579d8James Hawkins#include <utime.h> 23abd73e617970e2e4cb390d5f66cfd0dda57579d8James Hawkins#include <cstdlib> 24e8e8cf3f9509808b23506597d23212ac972d1393James Hawkins#include <string> 255cd4bc299b9a6ab5c19c4252bc4afc7f6701497aJames Hawkins#include <utility> 26eabe08b55eb7a8eceda02d302cea4d4f216e0d53James Hawkins#include <android-base/file.h> 27eabe08b55eb7a8eceda02d302cea4d4f216e0d53James Hawkins#include <android-base/logging.h> 28e8e8cf3f9509808b23506597d23212ac972d1393James Hawkins#include "histogram_logger.h" 29eef069acc58a55f8f9c028b6d9a737d7b1851354James Hawkins#include "uptime_parser.h" 30abd73e617970e2e4cb390d5f66cfd0dda57579d8James Hawkins 31abd73e617970e2e4cb390d5f66cfd0dda57579d8James Hawkinsnamespace { 32abd73e617970e2e4cb390d5f66cfd0dda57579d8James Hawkins 33abd73e617970e2e4cb390d5f66cfd0dda57579d8James Hawkinsconst char BOOTSTAT_DATA_DIR[] = "/data/misc/bootstat/"; 34abd73e617970e2e4cb390d5f66cfd0dda57579d8James Hawkins 35abd73e617970e2e4cb390d5f66cfd0dda57579d8James Hawkins// Given a boot even record file at |path|, extracts the event's relative time 36abd73e617970e2e4cb390d5f66cfd0dda57579d8James Hawkins// from the record into |uptime|. 37abd73e617970e2e4cb390d5f66cfd0dda57579d8James Hawkinsbool ParseRecordEventTime(const std::string& path, int32_t* uptime) { 38abd73e617970e2e4cb390d5f66cfd0dda57579d8James Hawkins DCHECK_NE(static_cast<int32_t*>(nullptr), uptime); 39abd73e617970e2e4cb390d5f66cfd0dda57579d8James Hawkins 40abd73e617970e2e4cb390d5f66cfd0dda57579d8James Hawkins struct stat file_stat; 41abd73e617970e2e4cb390d5f66cfd0dda57579d8James Hawkins if (stat(path.c_str(), &file_stat) == -1) { 42abd73e617970e2e4cb390d5f66cfd0dda57579d8James Hawkins PLOG(ERROR) << "Failed to read " << path; 43abd73e617970e2e4cb390d5f66cfd0dda57579d8James Hawkins return false; 44abd73e617970e2e4cb390d5f66cfd0dda57579d8James Hawkins } 45abd73e617970e2e4cb390d5f66cfd0dda57579d8James Hawkins 46abd73e617970e2e4cb390d5f66cfd0dda57579d8James Hawkins *uptime = file_stat.st_mtime; 47e8e8cf3f9509808b23506597d23212ac972d1393James Hawkins 48e8e8cf3f9509808b23506597d23212ac972d1393James Hawkins // The following code (till function exit) is a debug test to ensure the 49e8e8cf3f9509808b23506597d23212ac972d1393James Hawkins // validity of the file mtime value, i.e., to check that the record file 50e8e8cf3f9509808b23506597d23212ac972d1393James Hawkins // mtime values are not changed once set. 51e8e8cf3f9509808b23506597d23212ac972d1393James Hawkins // TODO(jhawkins): Remove this code. 52e8e8cf3f9509808b23506597d23212ac972d1393James Hawkins std::string content; 53e8e8cf3f9509808b23506597d23212ac972d1393James Hawkins if (!android::base::ReadFileToString(path, &content)) { 54e8e8cf3f9509808b23506597d23212ac972d1393James Hawkins PLOG(ERROR) << "Failed to read " << path; 55e8e8cf3f9509808b23506597d23212ac972d1393James Hawkins return false; 56e8e8cf3f9509808b23506597d23212ac972d1393James Hawkins } 57e8e8cf3f9509808b23506597d23212ac972d1393James Hawkins 586105846a0e6abe6be7d81675d13e40d4f0f6e840James Hawkins // Ignore existing bootstat records (which do not contain file content). 596105846a0e6abe6be7d81675d13e40d4f0f6e840James Hawkins if (!content.empty()) { 606105846a0e6abe6be7d81675d13e40d4f0f6e840James Hawkins int32_t value = std::stoi(content); 616105846a0e6abe6be7d81675d13e40d4f0f6e840James Hawkins bootstat::LogHistogram("bootstat_mtime_matches_content", value == *uptime); 626105846a0e6abe6be7d81675d13e40d4f0f6e840James Hawkins } 63e8e8cf3f9509808b23506597d23212ac972d1393James Hawkins 64abd73e617970e2e4cb390d5f66cfd0dda57579d8James Hawkins return true; 65abd73e617970e2e4cb390d5f66cfd0dda57579d8James Hawkins} 66abd73e617970e2e4cb390d5f66cfd0dda57579d8James Hawkins 67abd73e617970e2e4cb390d5f66cfd0dda57579d8James Hawkins} // namespace 68abd73e617970e2e4cb390d5f66cfd0dda57579d8James Hawkins 69abd73e617970e2e4cb390d5f66cfd0dda57579d8James HawkinsBootEventRecordStore::BootEventRecordStore() { 70abd73e617970e2e4cb390d5f66cfd0dda57579d8James Hawkins SetStorePath(BOOTSTAT_DATA_DIR); 71abd73e617970e2e4cb390d5f66cfd0dda57579d8James Hawkins} 72abd73e617970e2e4cb390d5f66cfd0dda57579d8James Hawkins 7335349148e18a3fb86cf3a3e253359af44a2396a8James Hawkinsvoid BootEventRecordStore::AddBootEvent(const std::string& event) { 74eef069acc58a55f8f9c028b6d9a737d7b1851354James Hawkins AddBootEventWithValue(event, bootstat::ParseUptime()); 7510f54be6d01c5e90540f4a83a76aef120bff782fJames Hawkins} 7610f54be6d01c5e90540f4a83a76aef120bff782fJames Hawkins 7710f54be6d01c5e90540f4a83a76aef120bff782fJames Hawkins// The implementation of AddBootEventValue makes use of the mtime file 7810f54be6d01c5e90540f4a83a76aef120bff782fJames Hawkins// attribute to store the value associated with a boot event in order to 7910f54be6d01c5e90540f4a83a76aef120bff782fJames Hawkins// optimize on-disk size requirements and small-file thrashing. 8010f54be6d01c5e90540f4a83a76aef120bff782fJames Hawkinsvoid BootEventRecordStore::AddBootEventWithValue( 8135349148e18a3fb86cf3a3e253359af44a2396a8James Hawkins const std::string& event, int32_t value) { 8235349148e18a3fb86cf3a3e253359af44a2396a8James Hawkins std::string record_path = GetBootEventPath(event); 83e8e8cf3f9509808b23506597d23212ac972d1393James Hawkins int record_fd = creat(record_path.c_str(), S_IRUSR | S_IWUSR); 84e8e8cf3f9509808b23506597d23212ac972d1393James Hawkins if (record_fd == -1) { 85abd73e617970e2e4cb390d5f66cfd0dda57579d8James Hawkins PLOG(ERROR) << "Failed to create " << record_path; 86e8e8cf3f9509808b23506597d23212ac972d1393James Hawkins return; 87e8e8cf3f9509808b23506597d23212ac972d1393James Hawkins } 88e8e8cf3f9509808b23506597d23212ac972d1393James Hawkins 89e8e8cf3f9509808b23506597d23212ac972d1393James Hawkins // Writing the value as content in the record file is a debug measure to 90e8e8cf3f9509808b23506597d23212ac972d1393James Hawkins // ensure the validity of the file mtime value, i.e., to check that the record 91e8e8cf3f9509808b23506597d23212ac972d1393James Hawkins // file mtime values are not changed once set. 92e8e8cf3f9509808b23506597d23212ac972d1393James Hawkins // TODO(jhawkins): Remove this block. 93e8e8cf3f9509808b23506597d23212ac972d1393James Hawkins if (!android::base::WriteStringToFd(std::to_string(value), record_fd)) { 94e8e8cf3f9509808b23506597d23212ac972d1393James Hawkins PLOG(ERROR) << "Failed to write value to " << record_path; 95e8e8cf3f9509808b23506597d23212ac972d1393James Hawkins close(record_fd); 96e8e8cf3f9509808b23506597d23212ac972d1393James Hawkins return; 97abd73e617970e2e4cb390d5f66cfd0dda57579d8James Hawkins } 98abd73e617970e2e4cb390d5f66cfd0dda57579d8James Hawkins 9910f54be6d01c5e90540f4a83a76aef120bff782fJames Hawkins // Fill out the stat structure for |record_path| in order to get the atime to 10010f54be6d01c5e90540f4a83a76aef120bff782fJames Hawkins // set in the utime() call. 101abd73e617970e2e4cb390d5f66cfd0dda57579d8James Hawkins struct stat file_stat; 102abd73e617970e2e4cb390d5f66cfd0dda57579d8James Hawkins if (stat(record_path.c_str(), &file_stat) == -1) { 103abd73e617970e2e4cb390d5f66cfd0dda57579d8James Hawkins PLOG(ERROR) << "Failed to read " << record_path; 104e8e8cf3f9509808b23506597d23212ac972d1393James Hawkins close(record_fd); 105e8e8cf3f9509808b23506597d23212ac972d1393James Hawkins return; 106abd73e617970e2e4cb390d5f66cfd0dda57579d8James Hawkins } 107abd73e617970e2e4cb390d5f66cfd0dda57579d8James Hawkins 10810f54be6d01c5e90540f4a83a76aef120bff782fJames Hawkins // Set the |modtime| of the file to store the value of the boot event while 10910f54be6d01c5e90540f4a83a76aef120bff782fJames Hawkins // preserving the |actime| (as read by stat). 11010f54be6d01c5e90540f4a83a76aef120bff782fJames Hawkins struct utimbuf times = {/* actime */ file_stat.st_atime, /* modtime */ value}; 111abd73e617970e2e4cb390d5f66cfd0dda57579d8James Hawkins if (utime(record_path.c_str(), ×) == -1) { 112abd73e617970e2e4cb390d5f66cfd0dda57579d8James Hawkins PLOG(ERROR) << "Failed to set mtime for " << record_path; 113e8e8cf3f9509808b23506597d23212ac972d1393James Hawkins close(record_fd); 114e8e8cf3f9509808b23506597d23212ac972d1393James Hawkins return; 115abd73e617970e2e4cb390d5f66cfd0dda57579d8James Hawkins } 116e8e8cf3f9509808b23506597d23212ac972d1393James Hawkins 117e8e8cf3f9509808b23506597d23212ac972d1393James Hawkins close(record_fd); 118abd73e617970e2e4cb390d5f66cfd0dda57579d8James Hawkins} 119abd73e617970e2e4cb390d5f66cfd0dda57579d8James Hawkins 12035349148e18a3fb86cf3a3e253359af44a2396a8James Hawkinsbool BootEventRecordStore::GetBootEvent( 12135349148e18a3fb86cf3a3e253359af44a2396a8James Hawkins const std::string& event, BootEventRecord* record) const { 12235349148e18a3fb86cf3a3e253359af44a2396a8James Hawkins CHECK_NE(static_cast<BootEventRecord*>(nullptr), record); 12335349148e18a3fb86cf3a3e253359af44a2396a8James Hawkins CHECK(!event.empty()); 12435349148e18a3fb86cf3a3e253359af44a2396a8James Hawkins 12535349148e18a3fb86cf3a3e253359af44a2396a8James Hawkins const std::string record_path = GetBootEventPath(event); 12635349148e18a3fb86cf3a3e253359af44a2396a8James Hawkins int32_t uptime; 12735349148e18a3fb86cf3a3e253359af44a2396a8James Hawkins if (!ParseRecordEventTime(record_path, &uptime)) { 12835349148e18a3fb86cf3a3e253359af44a2396a8James Hawkins LOG(ERROR) << "Failed to parse boot time record: " << record_path; 12935349148e18a3fb86cf3a3e253359af44a2396a8James Hawkins return false; 13035349148e18a3fb86cf3a3e253359af44a2396a8James Hawkins } 13135349148e18a3fb86cf3a3e253359af44a2396a8James Hawkins 13235349148e18a3fb86cf3a3e253359af44a2396a8James Hawkins *record = std::make_pair(event, uptime); 13335349148e18a3fb86cf3a3e253359af44a2396a8James Hawkins return true; 13435349148e18a3fb86cf3a3e253359af44a2396a8James Hawkins} 13535349148e18a3fb86cf3a3e253359af44a2396a8James Hawkins 136abd73e617970e2e4cb390d5f66cfd0dda57579d8James Hawkinsstd::vector<BootEventRecordStore::BootEventRecord> BootEventRecordStore:: 137abd73e617970e2e4cb390d5f66cfd0dda57579d8James Hawkins GetAllBootEvents() const { 138abd73e617970e2e4cb390d5f66cfd0dda57579d8James Hawkins std::vector<BootEventRecord> events; 139abd73e617970e2e4cb390d5f66cfd0dda57579d8James Hawkins 140abd73e617970e2e4cb390d5f66cfd0dda57579d8James Hawkins std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(store_path_.c_str()), closedir); 141abd73e617970e2e4cb390d5f66cfd0dda57579d8James Hawkins 142abd73e617970e2e4cb390d5f66cfd0dda57579d8James Hawkins // This case could happen due to external manipulation of the filesystem, 143abd73e617970e2e4cb390d5f66cfd0dda57579d8James Hawkins // so crash out if the record store doesn't exist. 144abd73e617970e2e4cb390d5f66cfd0dda57579d8James Hawkins CHECK_NE(static_cast<DIR*>(nullptr), dir.get()); 145abd73e617970e2e4cb390d5f66cfd0dda57579d8James Hawkins 146abd73e617970e2e4cb390d5f66cfd0dda57579d8James Hawkins struct dirent* entry; 147abd73e617970e2e4cb390d5f66cfd0dda57579d8James Hawkins while ((entry = readdir(dir.get())) != NULL) { 148abd73e617970e2e4cb390d5f66cfd0dda57579d8James Hawkins // Only parse regular files. 149abd73e617970e2e4cb390d5f66cfd0dda57579d8James Hawkins if (entry->d_type != DT_REG) { 150abd73e617970e2e4cb390d5f66cfd0dda57579d8James Hawkins continue; 151abd73e617970e2e4cb390d5f66cfd0dda57579d8James Hawkins } 152abd73e617970e2e4cb390d5f66cfd0dda57579d8James Hawkins 153abd73e617970e2e4cb390d5f66cfd0dda57579d8James Hawkins const std::string event = entry->d_name; 15435349148e18a3fb86cf3a3e253359af44a2396a8James Hawkins BootEventRecord record; 15535349148e18a3fb86cf3a3e253359af44a2396a8James Hawkins if (!GetBootEvent(event, &record)) { 15635349148e18a3fb86cf3a3e253359af44a2396a8James Hawkins LOG(ERROR) << "Failed to parse boot time event: " << event; 157abd73e617970e2e4cb390d5f66cfd0dda57579d8James Hawkins continue; 158abd73e617970e2e4cb390d5f66cfd0dda57579d8James Hawkins } 159abd73e617970e2e4cb390d5f66cfd0dda57579d8James Hawkins 16035349148e18a3fb86cf3a3e253359af44a2396a8James Hawkins events.push_back(record); 161abd73e617970e2e4cb390d5f66cfd0dda57579d8James Hawkins } 162abd73e617970e2e4cb390d5f66cfd0dda57579d8James Hawkins 163abd73e617970e2e4cb390d5f66cfd0dda57579d8James Hawkins return events; 164abd73e617970e2e4cb390d5f66cfd0dda57579d8James Hawkins} 165abd73e617970e2e4cb390d5f66cfd0dda57579d8James Hawkins 166abd73e617970e2e4cb390d5f66cfd0dda57579d8James Hawkinsvoid BootEventRecordStore::SetStorePath(const std::string& path) { 167abd73e617970e2e4cb390d5f66cfd0dda57579d8James Hawkins DCHECK_EQ('/', path.back()); 168abd73e617970e2e4cb390d5f66cfd0dda57579d8James Hawkins store_path_ = path; 169abd73e617970e2e4cb390d5f66cfd0dda57579d8James Hawkins} 170abd73e617970e2e4cb390d5f66cfd0dda57579d8James Hawkins 171abd73e617970e2e4cb390d5f66cfd0dda57579d8James Hawkinsstd::string BootEventRecordStore::GetBootEventPath( 172abd73e617970e2e4cb390d5f66cfd0dda57579d8James Hawkins const std::string& event) const { 173abd73e617970e2e4cb390d5f66cfd0dda57579d8James Hawkins DCHECK_EQ('/', store_path_.back()); 174abd73e617970e2e4cb390d5f66cfd0dda57579d8James Hawkins return store_path_ + event; 175abd73e617970e2e4cb390d5f66cfd0dda57579d8James Hawkins} 176