Merge "Add a config option to save the metrics data locally." into qt-dev
diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp
index df84b6a..a9f5208e 100644
--- a/cmds/statsd/src/StatsLogProcessor.cpp
+++ b/cmds/statsd/src/StatsLogProcessor.cpp
@@ -77,7 +77,6 @@
#define NS_PER_HOUR 3600 * NS_PER_SEC
-#define STATS_DATA_DIR "/data/misc/stats-data"
#define STATS_ACTIVE_METRIC_DIR "/data/misc/stats-active-metric"
// Cool down period for writing data to disk to avoid overwriting files.
@@ -106,6 +105,19 @@
StatsLogProcessor::~StatsLogProcessor() {
}
+static void flushProtoToBuffer(ProtoOutputStream& proto, vector<uint8_t>* outData) {
+ outData->clear();
+ outData->resize(proto.size());
+ size_t pos = 0;
+ sp<android::util::ProtoReader> reader = proto.data();
+ while (reader->readBuffer() != NULL) {
+ size_t toRead = reader->currentToRead();
+ std::memcpy(&((*outData)[pos]), reader->readBuffer(), toRead);
+ pos += toRead;
+ reader->move(toRead);
+ }
+}
+
void StatsLogProcessor::onAnomalyAlarmFired(
const int64_t& timestampNs,
unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>> alarmSet) {
@@ -366,25 +378,29 @@
proto->end(configKeyToken);
// End of ConfigKey.
+ bool keepFile = false;
+ auto it = mMetricsManagers.find(key);
+ if (it != mMetricsManagers.end() && it->second->shouldPersistLocalHistory()) {
+ keepFile = true;
+ }
+
// Then, check stats-data directory to see there's any file containing
// ConfigMetricsReport from previous shutdowns to concatenate to reports.
- StorageManager::appendConfigMetricsReport(key, proto, erase_data);
+ StorageManager::appendConfigMetricsReport(
+ key, proto, erase_data && !keepFile /* should remove file after appending it */,
+ dumpReportReason == ADB_DUMP /*if caller is adb*/);
- auto it = mMetricsManagers.find(key);
if (it != mMetricsManagers.end()) {
// This allows another broadcast to be sent within the rate-limit period if we get close to
// filling the buffer again soon.
mLastBroadcastTimes.erase(key);
- // Start of ConfigMetricsReport (reports).
- uint64_t reportsToken =
- proto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_REPORTS);
- onConfigMetricsReportLocked(key, dumpTimeStampNs,
- include_current_partial_bucket,
- erase_data, dumpReportReason,
- dumpLatency, proto);
- proto->end(reportsToken);
- // End of ConfigMetricsReport (reports).
+ vector<uint8_t> buffer;
+ onConfigMetricsReportLocked(key, dumpTimeStampNs, include_current_partial_bucket,
+ erase_data, dumpReportReason, dumpLatency,
+ false /* is this data going to be saved on disk */, &buffer);
+ proto->write(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_REPORTS,
+ reinterpret_cast<char*>(buffer.data()), buffer.size());
} else {
ALOGW("Config source %s does not exist", key.ToString().c_str());
}
@@ -404,16 +420,8 @@
dumpReportReason, dumpLatency, &proto);
if (outData != nullptr) {
- outData->clear();
- outData->resize(proto.size());
- size_t pos = 0;
- sp<android::util::ProtoReader> reader = proto.data();
- while (reader->readBuffer() != NULL) {
- size_t toRead = reader->currentToRead();
- std::memcpy(&((*outData)[pos]), reader->readBuffer(), toRead);
- pos += toRead;
- reader->move(toRead);
- }
+ flushProtoToBuffer(proto, outData);
+ VLOG("output data size %zu", outData->size());
}
StatsdStats::getInstance().noteMetricsReportSent(key, proto.size());
@@ -422,13 +430,11 @@
/*
* onConfigMetricsReportLocked dumps serialized ConfigMetricsReport into outData.
*/
-void StatsLogProcessor::onConfigMetricsReportLocked(const ConfigKey& key,
- const int64_t dumpTimeStampNs,
- const bool include_current_partial_bucket,
- const bool erase_data,
- const DumpReportReason dumpReportReason,
- const DumpLatency dumpLatency,
- ProtoOutputStream* proto) {
+void StatsLogProcessor::onConfigMetricsReportLocked(
+ const ConfigKey& key, const int64_t dumpTimeStampNs,
+ const bool include_current_partial_bucket, const bool erase_data,
+ const DumpReportReason dumpReportReason, const DumpLatency dumpLatency,
+ const bool dataSavedOnDisk, vector<uint8_t>* buffer) {
// We already checked whether key exists in mMetricsManagers in
// WriteDataToDisk.
auto it = mMetricsManagers.find(key);
@@ -440,35 +446,46 @@
std::set<string> str_set;
+ ProtoOutputStream tempProto;
// First, fill in ConfigMetricsReport using current data on memory, which
// starts from filling in StatsLogReport's.
- it->second->onDumpReport(dumpTimeStampNs, include_current_partial_bucket,
- erase_data, dumpLatency, &str_set, proto);
+ it->second->onDumpReport(dumpTimeStampNs, include_current_partial_bucket, erase_data,
+ dumpLatency, &str_set, &tempProto);
// Fill in UidMap if there is at least one metric to report.
// This skips the uid map if it's an empty config.
if (it->second->getNumMetrics() > 0) {
- uint64_t uidMapToken = proto->start(FIELD_TYPE_MESSAGE | FIELD_ID_UID_MAP);
+ uint64_t uidMapToken = tempProto.start(FIELD_TYPE_MESSAGE | FIELD_ID_UID_MAP);
mUidMap->appendUidMap(
dumpTimeStampNs, key, it->second->hashStringInReport() ? &str_set : nullptr,
- it->second->versionStringsInReport(), it->second->installerInReport(), proto);
- proto->end(uidMapToken);
+ it->second->versionStringsInReport(), it->second->installerInReport(), &tempProto);
+ tempProto.end(uidMapToken);
}
// Fill in the timestamps.
- proto->write(FIELD_TYPE_INT64 | FIELD_ID_LAST_REPORT_ELAPSED_NANOS,
- (long long)lastReportTimeNs);
- proto->write(FIELD_TYPE_INT64 | FIELD_ID_CURRENT_REPORT_ELAPSED_NANOS,
- (long long)dumpTimeStampNs);
- proto->write(FIELD_TYPE_INT64 | FIELD_ID_LAST_REPORT_WALL_CLOCK_NANOS,
- (long long)lastReportWallClockNs);
- proto->write(FIELD_TYPE_INT64 | FIELD_ID_CURRENT_REPORT_WALL_CLOCK_NANOS,
- (long long)getWallClockNs());
+ tempProto.write(FIELD_TYPE_INT64 | FIELD_ID_LAST_REPORT_ELAPSED_NANOS,
+ (long long)lastReportTimeNs);
+ tempProto.write(FIELD_TYPE_INT64 | FIELD_ID_CURRENT_REPORT_ELAPSED_NANOS,
+ (long long)dumpTimeStampNs);
+ tempProto.write(FIELD_TYPE_INT64 | FIELD_ID_LAST_REPORT_WALL_CLOCK_NANOS,
+ (long long)lastReportWallClockNs);
+ tempProto.write(FIELD_TYPE_INT64 | FIELD_ID_CURRENT_REPORT_WALL_CLOCK_NANOS,
+ (long long)getWallClockNs());
// Dump report reason
- proto->write(FIELD_TYPE_INT32 | FIELD_ID_DUMP_REPORT_REASON, dumpReportReason);
+ tempProto.write(FIELD_TYPE_INT32 | FIELD_ID_DUMP_REPORT_REASON, dumpReportReason);
for (const auto& str : str_set) {
- proto->write(FIELD_TYPE_STRING | FIELD_COUNT_REPEATED | FIELD_ID_STRINGS, str);
+ tempProto.write(FIELD_TYPE_STRING | FIELD_COUNT_REPEATED | FIELD_ID_STRINGS, str);
+ }
+
+ flushProtoToBuffer(tempProto, buffer);
+
+ // save buffer to disk if needed
+ if (erase_data && !dataSavedOnDisk && it->second->shouldPersistLocalHistory()) {
+ VLOG("save history to disk");
+ string file_name = StorageManager::getDataHistoryFileName((long)getWallClockSec(),
+ key.GetUid(), key.GetId());
+ StorageManager::writeFile(file_name.c_str(), buffer->data(), buffer->size());
}
}
@@ -584,18 +601,14 @@
!mMetricsManagers.find(key)->second->shouldWriteToDisk()) {
return;
}
- ProtoOutputStream proto;
+ vector<uint8_t> buffer;
onConfigMetricsReportLocked(key, timestampNs, true /* include_current_partial_bucket*/,
- true /* erase_data */, dumpReportReason, dumpLatency, &proto);
- string file_name = StringPrintf("%s/%ld_%d_%lld", STATS_DATA_DIR,
- (long)getWallClockSec(), key.GetUid(), (long long)key.GetId());
- android::base::unique_fd fd(open(file_name.c_str(),
- O_WRONLY | O_CREAT | O_CLOEXEC, S_IRUSR | S_IWUSR));
- if (fd == -1) {
- ALOGE("Attempt to write %s but failed", file_name.c_str());
- return;
- }
- proto.flush(fd.get());
+ true /* erase_data */, dumpReportReason, dumpLatency, true,
+ &buffer);
+ string file_name =
+ StorageManager::getDataFileName((long)getWallClockSec(), key.GetUid(), key.GetId());
+ StorageManager::writeFile(file_name.c_str(), buffer.data(), buffer.size());
+
// We were able to write the ConfigMetricsReport to disk, so we should trigger collection ASAP.
mOnDiskDataConfigs.insert(key);
}
diff --git a/cmds/statsd/src/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h
index 305a4ce..f4db0af 100644
--- a/cmds/statsd/src/StatsLogProcessor.h
+++ b/cmds/statsd/src/StatsLogProcessor.h
@@ -164,12 +164,13 @@
const DumpReportReason dumpReportReason,
const DumpLatency dumpLatency);
- void onConfigMetricsReportLocked(const ConfigKey& key, const int64_t dumpTimeStampNs,
- const bool include_current_partial_bucket,
- const bool erase_data,
- const DumpReportReason dumpReportReason,
- const DumpLatency dumpLatency,
- util::ProtoOutputStream* proto);
+ void onConfigMetricsReportLocked(
+ const ConfigKey& key, const int64_t dumpTimeStampNs,
+ const bool include_current_partial_bucket, const bool erase_data,
+ const DumpReportReason dumpReportReason, const DumpLatency dumpLatency,
+ /*if dataSavedToDisk is true, it indicates the caller will write the data to disk
+ (e.g., before reboot). So no need to further persist local history.*/
+ const bool dataSavedToDisk, vector<uint8_t>* proto);
/* Check if we should send a broadcast if approaching memory limits and if we're over, we
* actually delete the data. */
diff --git a/cmds/statsd/src/guardrail/StatsdStats.h b/cmds/statsd/src/guardrail/StatsdStats.h
index 53f12ac..4d21a29 100644
--- a/cmds/statsd/src/guardrail/StatsdStats.h
+++ b/cmds/statsd/src/guardrail/StatsdStats.h
@@ -145,6 +145,9 @@
// Maximum age (30 days) that files on disk can exist in seconds.
static const int kMaxAgeSecond = 60 * 60 * 24 * 30;
+ // Maximum age (2 days) that local history files on disk can exist in seconds.
+ static const int kMaxLocalHistoryAgeSecond = 60 * 60 * 24 * 2;
+
// Maximum number of files (1000) that can be in stats directory on disk.
static const int kMaxFileNumber = 1000;
diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp
index 095f9dd..6a55289 100644
--- a/cmds/statsd/src/metrics/MetricsManager.cpp
+++ b/cmds/statsd/src/metrics/MetricsManager.cpp
@@ -65,7 +65,8 @@
mTtlNs(config.has_ttl_in_seconds() ? config.ttl_in_seconds() * NS_PER_SEC : -1),
mTtlEndNs(-1),
mLastReportTimeNs(currentTimeNs),
- mLastReportWallClockNs(getWallClockNs()) {
+ mLastReportWallClockNs(getWallClockNs()),
+ mShouldPersistHistory(config.persist_locally()) {
// Init the ttl end timestamp.
refreshTtl(timeBaseNs);
diff --git a/cmds/statsd/src/metrics/MetricsManager.h b/cmds/statsd/src/metrics/MetricsManager.h
index d317f8e..00ae3b7 100644
--- a/cmds/statsd/src/metrics/MetricsManager.h
+++ b/cmds/statsd/src/metrics/MetricsManager.h
@@ -78,6 +78,10 @@
return mNoReportMetricIds.size() != mAllMetricProducers.size();
}
+ bool shouldPersistLocalHistory() const {
+ return mShouldPersistHistory;
+ }
+
void dumpStates(FILE* out, bool verbose);
inline bool isInTtl(const int64_t timestampNs) const {
@@ -184,6 +188,8 @@
// Contains the annotations passed in with StatsdConfig.
std::list<std::pair<const int64_t, const int32_t>> mAnnotations;
+ const bool mShouldPersistHistory;
+
// To guard access to mAllowedLogSources
mutable std::mutex mAllowedLogSourcesMutex;
diff --git a/cmds/statsd/src/statsd_config.proto b/cmds/statsd/src/statsd_config.proto
index 257e65e..2260b9b 100644
--- a/cmds/statsd/src/statsd_config.proto
+++ b/cmds/statsd/src/statsd_config.proto
@@ -439,6 +439,8 @@
optional bool installer_in_metric_report = 19;
+ optional bool persist_locally = 20 [default = false];
+
// Field number 1000 is reserved for later use.
reserved 1000;
}
diff --git a/cmds/statsd/src/storage/StorageManager.cpp b/cmds/statsd/src/storage/StorageManager.cpp
index cf8b974..0a9161d 100644
--- a/cmds/statsd/src/storage/StorageManager.cpp
+++ b/cmds/statsd/src/storage/StorageManager.cpp
@@ -56,9 +56,31 @@
using android::base::StringPrintf;
using std::unique_ptr;
-// Returns array of int64_t which contains timestamp in seconds, uid, and
-// configID.
-static void parseFileName(char* name, int64_t* result) {
+struct FileName {
+ int64_t mTimestampSec;
+ int mUid;
+ int64_t mConfigId;
+ bool mIsHistory;
+ string getFullFileName(const char* path) {
+ return StringPrintf("%s/%lld_%d_%lld%s", path, (long long)mTimestampSec, (int)mUid,
+ (long long)mConfigId, (mIsHistory ? "_history" : ""));
+ };
+};
+
+string StorageManager::getDataFileName(long wallClockSec, int uid, int64_t id) {
+ return StringPrintf("%s/%ld_%d_%lld", STATS_DATA_DIR, wallClockSec, uid,
+ (long long)id);
+}
+
+string StorageManager::getDataHistoryFileName(long wallClockSec, int uid, int64_t id) {
+ return StringPrintf("%s/%ld_%d_%lld_history", STATS_DATA_DIR, wallClockSec, uid,
+ (long long)id);
+}
+
+// Returns array of int64_t which contains timestamp in seconds, uid,
+// configID and whether the file is a local history file.
+static void parseFileName(char* name, FileName* output) {
+ int64_t result[3];
int index = 0;
char* substr = strtok(name, "_");
while (substr != nullptr && index < 3) {
@@ -72,11 +94,12 @@
if (index < 3) {
result[0] = -1;
}
-}
-static string getFilePath(const char* path, int64_t timestamp, int64_t uid, int64_t configID) {
- return StringPrintf("%s/%lld_%d_%lld", path, (long long)timestamp, (int)uid,
- (long long)configID);
+ output->mTimestampSec = result[0];
+ output->mUid = result[1];
+ output->mConfigId = result[2];
+ // check if the file is a local history.
+ output->mIsHistory = (substr != nullptr && strcmp("history", substr) == 0);
}
void StorageManager::writeFile(const char* file, const void* buffer, int numBytes) {
@@ -88,14 +111,13 @@
trimToFit(STATS_SERVICE_DIR);
trimToFit(STATS_DATA_DIR);
- int result = write(fd, buffer, numBytes);
- if (result == numBytes) {
+ if (android::base::WriteFully(fd, buffer, numBytes)) {
VLOG("Successfully wrote %s", file);
} else {
- VLOG("Failed to write %s", file);
+ ALOGE("Failed to write %s", file);
}
- result = fchown(fd, AID_STATSD, AID_STATSD);
+ int result = fchown(fd, AID_STATSD, AID_STATSD);
if (result) {
VLOG("Failed to chown %s to statsd", file);
}
@@ -349,13 +371,10 @@
if (name[0] == '.') continue;
VLOG("file %s", name);
- int64_t result[3];
- parseFileName(name, result);
- if (result[0] == -1) continue;
- int64_t uid = result[1];
- int64_t configID = result[2];
-
- sendBroadcast(ConfigKey((int)uid, configID));
+ FileName output;
+ parseFileName(name, &output);
+ if (output.mTimestampSec == -1 || output.mIsHistory) continue;
+ sendBroadcast(ConfigKey((int)output.mUid, output.mConfigId));
}
}
@@ -378,55 +397,58 @@
if (suffixLen <= nameLen &&
strncmp(name + nameLen - suffixLen, suffix.c_str(), suffixLen) == 0) {
// Check again that the file name is parseable.
- int64_t result[3];
- parseFileName(name, result);
- if (result[0] == -1) continue;
+ FileName output;
+ parseFileName(name, &output);
+ if (output.mTimestampSec == -1 || output.mIsHistory) continue;
return true;
}
}
return false;
}
-void StorageManager::appendConfigMetricsReport(const ConfigKey& key,
- ProtoOutputStream* proto,
- bool erasa_data) {
+void StorageManager::appendConfigMetricsReport(const ConfigKey& key, ProtoOutputStream* proto,
+ bool erase_data, bool isAdb) {
unique_ptr<DIR, decltype(&closedir)> dir(opendir(STATS_DATA_DIR), closedir);
if (dir == NULL) {
VLOG("Path %s does not exist", STATS_DATA_DIR);
return;
}
- string suffix = StringPrintf("%d_%lld", key.GetUid(), (long long)key.GetId());
-
dirent* de;
while ((de = readdir(dir.get()))) {
char* name = de->d_name;
+ string fileName(name);
if (name[0] == '.') continue;
+ FileName output;
+ parseFileName(name, &output);
- size_t nameLen = strlen(name);
- size_t suffixLen = suffix.length();
- if (suffixLen <= nameLen &&
- strncmp(name + nameLen - suffixLen, suffix.c_str(), suffixLen) == 0) {
- int64_t result[3];
- parseFileName(name, result);
- if (result[0] == -1) continue;
- int64_t timestamp = result[0];
- int64_t uid = result[1];
- int64_t configID = result[2];
+ if (output.mTimestampSec == -1 || (output.mIsHistory && !isAdb) ||
+ output.mUid != key.GetUid() || output.mConfigId != key.GetId()) {
+ continue;
+ }
- string file_name = getFilePath(STATS_DATA_DIR, timestamp, uid, configID);
- int fd = open(file_name.c_str(), O_RDONLY | O_CLOEXEC);
- if (fd != -1) {
- string content;
- if (android::base::ReadFdToString(fd, &content)) {
- proto->write(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_REPORTS,
- content.c_str(), content.size());
- }
- close(fd);
+ auto fullPathName = StringPrintf("%s/%s", STATS_DATA_DIR, fileName.c_str());
+ int fd = open(fullPathName.c_str(), O_RDONLY | O_CLOEXEC);
+ if (fd != -1) {
+ string content;
+ if (android::base::ReadFdToString(fd, &content)) {
+ proto->write(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_REPORTS,
+ content.c_str(), content.size());
}
+ close(fd);
+ } else {
+ ALOGE("file cannot be opened");
+ }
- if (erasa_data) {
- remove(file_name.c_str());
+ if (erase_data) {
+ remove(fullPathName.c_str());
+ } else if (output.mIsHistory && !isAdb) {
+ // This means a real data owner has called to get this data. But the config says it
+ // wants to keep a local history. So now this file must be renamed as a history file.
+ // So that next time, when owner calls getData() again, this data won't be uploaded
+ // again. rename returns 0 on success
+ if (rename(fullPathName.c_str(), (fullPathName + "_history").c_str())) {
+ ALOGE("Failed to rename file %s", fullPathName.c_str());
}
}
}
@@ -458,23 +480,20 @@
while ((de = readdir(dir.get()))) {
char* name = de->d_name;
if (name[0] == '.') continue;
- VLOG("file %s", name);
- int64_t result[3];
- parseFileName(name, result);
- if (result[0] == -1) continue;
- int64_t timestamp = result[0];
- int64_t uid = result[1];
- int64_t configID = result[2];
- string file_name = getFilePath(STATS_SERVICE_DIR, timestamp, uid, configID);
+ FileName output;
+ parseFileName(name, &output);
+ if (output.mTimestampSec == -1) continue;
+ string file_name = output.getFullFileName(STATS_SERVICE_DIR);
int fd = open(file_name.c_str(), O_RDONLY | O_CLOEXEC);
if (fd != -1) {
string content;
if (android::base::ReadFdToString(fd, &content)) {
StatsdConfig config;
if (config.ParseFromString(content)) {
- configsMap[ConfigKey(uid, configID)] = config;
- VLOG("map key uid=%lld|configID=%lld", (long long)uid, (long long)configID);
+ configsMap[ConfigKey(output.mUid, output.mConfigId)] = config;
+ VLOG("map key uid=%lld|configID=%lld", (long long)output.mUid,
+ (long long)output.mConfigId);
}
}
close(fd);
@@ -533,6 +552,30 @@
return false;
}
+void StorageManager::sortFiles(vector<FileInfo>* fileNames) {
+ // Reverse sort to effectively remove from the back (oldest entries).
+ // This will sort files in reverse-chronological order. Local history files have lower
+ // priority than regular data files.
+ sort(fileNames->begin(), fileNames->end(), [](FileInfo& lhs, FileInfo& rhs) {
+ // first consider if the file is a local history
+ if (lhs.mIsHistory && !rhs.mIsHistory) {
+ return false;
+ } else if (rhs.mIsHistory && !lhs.mIsHistory) {
+ return true;
+ }
+
+ // then consider the age.
+ if (lhs.mFileAgeSec < rhs.mFileAgeSec) {
+ return true;
+ } else if (lhs.mFileAgeSec > rhs.mFileAgeSec) {
+ return false;
+ }
+
+ // then good luck.... use string::compare
+ return lhs.mFileName.compare(rhs.mFileName) > 0;
+ });
+}
+
void StorageManager::trimToFit(const char* path) {
unique_ptr<DIR, decltype(&closedir)> dir(opendir(path), closedir);
if (dir == NULL) {
@@ -541,55 +584,46 @@
}
dirent* de;
int totalFileSize = 0;
- vector<string> fileNames;
+ vector<FileInfo> fileNames;
+ auto nowSec = getWallClockSec();
while ((de = readdir(dir.get()))) {
char* name = de->d_name;
if (name[0] == '.') continue;
- int64_t result[3];
- parseFileName(name, result);
- if (result[0] == -1) continue;
- int64_t timestamp = result[0];
- int64_t uid = result[1];
- int64_t configID = result[2];
- string file_name = getFilePath(path, timestamp, uid, configID);
+ FileName output;
+ parseFileName(name, &output);
+ if (output.mTimestampSec == -1) continue;
+ string file_name = output.getFullFileName(path);
// Check for timestamp and delete if it's too old.
- long fileAge = getWallClockSec() - timestamp;
- if (fileAge > StatsdStats::kMaxAgeSecond) {
+ long fileAge = nowSec - output.mTimestampSec;
+ if (fileAge > StatsdStats::kMaxAgeSecond ||
+ (output.mIsHistory && fileAge > StatsdStats::kMaxLocalHistoryAgeSecond)) {
deleteFile(file_name.c_str());
+ continue;
}
- fileNames.push_back(file_name);
ifstream file(file_name.c_str(), ifstream::in | ifstream::binary);
+ int fileSize = 0;
if (file.is_open()) {
file.seekg(0, ios::end);
- int fileSize = file.tellg();
+ fileSize = file.tellg();
file.close();
totalFileSize += fileSize;
}
+ fileNames.emplace_back(file_name, output.mIsHistory, fileSize, fileAge);
}
if (fileNames.size() > StatsdStats::kMaxFileNumber ||
totalFileSize > StatsdStats::kMaxFileSize) {
- // Reverse sort to effectively remove from the back (oldest entries).
- // This will sort files in reverse-chronological order.
- sort(fileNames.begin(), fileNames.end(), std::greater<std::string>());
+ sortFiles(&fileNames);
}
// Start removing files from oldest to be under the limit.
while (fileNames.size() > 0 && (fileNames.size() > StatsdStats::kMaxFileNumber ||
totalFileSize > StatsdStats::kMaxFileSize)) {
- string file_name = fileNames.at(fileNames.size() - 1);
- ifstream file(file_name.c_str(), ifstream::in | ifstream::binary);
- if (file.is_open()) {
- file.seekg(0, ios::end);
- int fileSize = file.tellg();
- file.close();
- totalFileSize -= fileSize;
- }
-
- deleteFile(file_name.c_str());
+ totalFileSize -= fileNames.at(fileNames.size() - 1).mFileSizeBytes;
+ deleteFile(fileNames.at(fileNames.size() - 1).mFileName.c_str());
fileNames.pop_back();
}
}
@@ -614,15 +648,13 @@
if (name[0] == '.') {
continue;
}
- int64_t result[3];
- parseFileName(name, result);
- if (result[0] == -1) continue;
- int64_t timestamp = result[0];
- int64_t uid = result[1];
- int64_t configID = result[2];
- dprintf(outFd, "\t #%d, Last updated: %lld, UID: %d, Config ID: %lld", fileCount + 1,
- (long long)timestamp, (int)uid, (long long)configID);
- string file_name = getFilePath(path, timestamp, uid, configID);
+ FileName output;
+ parseFileName(name, &output);
+ if (output.mTimestampSec == -1) continue;
+ dprintf(outFd, "\t #%d, Last updated: %lld, UID: %d, Config ID: %lld, %s", fileCount + 1,
+ (long long)output.mTimestampSec, output.mUid, (long long)output.mConfigId,
+ (output.mIsHistory ? "local history" : ""));
+ string file_name = output.getFullFileName(path);
ifstream file(file_name.c_str(), ifstream::in | ifstream::binary);
if (file.is_open()) {
file.seekg(0, ios::end);
diff --git a/cmds/statsd/src/storage/StorageManager.h b/cmds/statsd/src/storage/StorageManager.h
index dfcea65..69b41c2 100644
--- a/cmds/statsd/src/storage/StorageManager.h
+++ b/cmds/statsd/src/storage/StorageManager.h
@@ -31,6 +31,19 @@
class StorageManager : public virtual RefBase {
public:
+ struct FileInfo {
+ FileInfo(std::string name, bool isHistory, int fileSize, long fileAge)
+ : mFileName(name),
+ mIsHistory(isHistory),
+ mFileSizeBytes(fileSize),
+ mFileAgeSec(fileAge) {
+ }
+ std::string mFileName;
+ bool mIsHistory;
+ int mFileSizeBytes;
+ long mFileAgeSec;
+ };
+
/**
* Writes a given byte array as a file to the specified file path.
*/
@@ -81,10 +94,19 @@
/**
* Appends the ConfigMetricsReport found on disk to the specifid proto
* and, if erase_data, deletes it from disk.
+ *
+ * [isAdb]: if the caller is adb dump. This includes local adb dump or dumpsys by
+ * bugreport or incidentd. When true, we will append any local history data too.
+ *
+ * When
+ * erase_data=true, isAdb=true: append history data to output, remove all data after read
+ * erase_data=false, isAdb=true: append history data to output, keep data after read
+ * erase_data=true, isAdb=false: do not append history data, and remove data after read
+ * erase_data=false, isAdb=false: do not append history data and *rename* all data files to
+ * history files.
*/
- static void appendConfigMetricsReport(const ConfigKey& key,
- ProtoOutputStream* proto,
- bool erase_data);
+ static void appendConfigMetricsReport(const ConfigKey& key, ProtoOutputStream* proto,
+ bool erase_data, bool isAdb);
/**
* Call to load the saved configs from disk.
@@ -115,6 +137,12 @@
*/
static void printStats(int out);
+ static string getDataFileName(long wallClockSec, int uid, int64_t id);
+
+ static string getDataHistoryFileName(long wallClockSec, int uid, int64_t id);
+
+ static void sortFiles(vector<FileInfo>* fileNames);
+
private:
/**
* Prints disk usage statistics about a directory related to statsd.
diff --git a/cmds/statsd/tests/storage/StorageManager_test.cpp b/cmds/statsd/tests/storage/StorageManager_test.cpp
index 4564a5d..cae2f30 100644
--- a/cmds/statsd/tests/storage/StorageManager_test.cpp
+++ b/cmds/statsd/tests/storage/StorageManager_test.cpp
@@ -110,6 +110,21 @@
EXPECT_EQ(trainInfo.experimentIds, trainInfoResult.experimentIds);
}
+TEST(StorageManagerTest, SortFileTest) {
+ vector<StorageManager::FileInfo> list;
+ // assume now sec is 500
+ list.emplace_back("200_5000_123454", false, 20, 300);
+ list.emplace_back("300_2000_123454_history", true, 30, 200);
+ list.emplace_back("400_100009_123454_history", true, 40, 100);
+ list.emplace_back("100_2000_123454", false, 50, 400);
+
+ StorageManager::sortFiles(&list);
+ EXPECT_EQ("200_5000_123454", list[0].mFileName);
+ EXPECT_EQ("100_2000_123454", list[1].mFileName);
+ EXPECT_EQ("400_100009_123454_history", list[2].mFileName);
+ EXPECT_EQ("300_2000_123454_history", list[3].mFileName);
+}
+
} // namespace statsd
} // namespace os
} // namespace android