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(), &times) == -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