Home | History | Annotate | Download | only in bootstat
      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 
     32 namespace {
     33 
     34 const 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|.
     38 bool 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 
     72 BootEventRecordStore::BootEventRecordStore() {
     73   SetStorePath(BOOTSTAT_DATA_DIR);
     74 }
     75 
     76 void 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.
     83 void 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(), &times) == -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 
    123 bool 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 
    139 std::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 
    169 void BootEventRecordStore::SetStorePath(const std::string& path) {
    170   DCHECK_EQ('/', path.back());
    171   store_path_ = path;
    172 }
    173 
    174 std::string BootEventRecordStore::GetBootEventPath(
    175     const std::string& event) const {
    176   DCHECK_EQ('/', store_path_.back());
    177   return store_path_ + event;
    178 }
    179