Implement Cpu Freq Section
When poll from sysfs, revents return POLLERR by default, handles this edge case in this cl. Bug: 68774444 Test: unit tested + on device tests Change-Id: I23540299c026d3e7676497f56690e9f8646a47bd
This commit is contained in:
parent
a379f499c8
commit
0eb223496c
@ -58,6 +58,7 @@ cc_library {
|
||||
// runtime, as well as the only protos that are actually
|
||||
// needed by the device.
|
||||
srcs: [
|
||||
"core/proto/android/os/cpufreq.proto",
|
||||
"core/proto/android/os/cpuinfo.proto",
|
||||
"core/proto/android/os/kernelwake.proto",
|
||||
"core/proto/android/os/pagetypeinfo.proto",
|
||||
@ -82,6 +83,7 @@ gensrcs {
|
||||
],
|
||||
|
||||
srcs: [
|
||||
"core/proto/android/os/cpufreq.proto",
|
||||
"core/proto/android/os/cpuinfo.proto",
|
||||
"core/proto/android/os/kernelwake.proto",
|
||||
"core/proto/android/os/pagetypeinfo.proto",
|
||||
|
@ -16,6 +16,7 @@
|
||||
|
||||
#define LOG_TAG "incident_helper"
|
||||
|
||||
#include "parsers/CpuFreqParser.h"
|
||||
#include "parsers/CpuInfoParser.h"
|
||||
#include "parsers/KernelWakesParser.h"
|
||||
#include "parsers/PageTypeInfoParser.h"
|
||||
@ -57,6 +58,8 @@ static TextParserBase* selectParser(int section) {
|
||||
return new KernelWakesParser();
|
||||
case 2003:
|
||||
return new CpuInfoParser();
|
||||
case 2004:
|
||||
return new CpuFreqParser();
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
|
90
cmds/incident_helper/src/parsers/CpuFreqParser.cpp
Normal file
90
cmds/incident_helper/src/parsers/CpuFreqParser.cpp
Normal file
@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Copyright (C) 2017 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
#define LOG_TAG "incident_helper"
|
||||
|
||||
#include <android/util/ProtoOutputStream.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "frameworks/base/core/proto/android/os/cpufreq.proto.h"
|
||||
#include "ih_util.h"
|
||||
#include "CpuFreqParser.h"
|
||||
|
||||
using namespace android::os;
|
||||
|
||||
status_t
|
||||
CpuFreqParser::Parse(const int in, const int out) const
|
||||
{
|
||||
Reader reader(in);
|
||||
string line;
|
||||
|
||||
// parse header
|
||||
reader.readLine(&line);
|
||||
header_t header = parseHeader(line, TAB_DELIMITER);
|
||||
if (header.size() < 1) {
|
||||
fprintf(stderr, "Bad header: %s\n", line.c_str());
|
||||
return BAD_VALUE;
|
||||
}
|
||||
const int numCpus = (int)header.size() - 1;
|
||||
vector<pair<int, long long>> cpucores[numCpus];
|
||||
|
||||
// parse freq and time
|
||||
while (reader.readLine(&line)) {
|
||||
if (line.empty()) continue;
|
||||
|
||||
record_t record = parseRecord(line, TAB_DELIMITER);
|
||||
if (record.size() != header.size()) {
|
||||
fprintf(stderr, "Bad line: %s\n", line.c_str());
|
||||
continue;
|
||||
}
|
||||
|
||||
int freq = toInt(record[0]);
|
||||
for (int i=0; i<numCpus; i++) {
|
||||
if (strcmp(record[i+1].c_str(), "N/A") == 0) {
|
||||
continue;
|
||||
}
|
||||
cpucores[i].push_back(make_pair(freq, toLongLong(record[i+1])));
|
||||
}
|
||||
}
|
||||
|
||||
ProtoOutputStream proto;
|
||||
|
||||
long jiffyHz = sysconf(_SC_CLK_TCK);
|
||||
proto.write(CpuFreq::JIFFY_HZ, (int)jiffyHz);
|
||||
|
||||
for (int i=0; i<numCpus; i++) {
|
||||
long long token = proto.start(CpuFreq::CPU_FREQS);
|
||||
proto.write(CpuFreqStats::CPU_NAME, header[i+1]);
|
||||
for (vector<pair<int, long long>>::iterator it = cpucores[i].begin(); it != cpucores[i].end(); it++) {
|
||||
long long stateToken = proto.start(CpuFreqStats::TIMES);
|
||||
proto.write(CpuFreqStats::TimeInState::STATE_KHZ, it->first);
|
||||
proto.write(CpuFreqStats::TimeInState::TIME_JIFFY, it->second);
|
||||
proto.end(stateToken);
|
||||
}
|
||||
proto.end(token);
|
||||
}
|
||||
|
||||
if (!reader.ok(&line)) {
|
||||
fprintf(stderr, "Bad read from fd %d: %s\n", in, line.c_str());
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!proto.flush(out)) {
|
||||
fprintf(stderr, "[%s]Error writing proto back\n", this->name.string());
|
||||
return -1;
|
||||
}
|
||||
fprintf(stderr, "[%s]Proto size: %zu bytes\n", this->name.string(), proto.size());
|
||||
return NO_ERROR;
|
||||
}
|
35
cmds/incident_helper/src/parsers/CpuFreqParser.h
Normal file
35
cmds/incident_helper/src/parsers/CpuFreqParser.h
Normal file
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright (C) 2017 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef CPU_FREQ_PARSER_H
|
||||
#define CPU_FREQ_PARSER_H
|
||||
|
||||
#include "TextParserBase.h"
|
||||
|
||||
using namespace android;
|
||||
|
||||
/**
|
||||
* Cpu frequency parser, parses text in /sys/devices/system/cpu/cpufreq/all_time_in_state
|
||||
*/
|
||||
class CpuFreqParser : public TextParserBase {
|
||||
public:
|
||||
CpuFreqParser() : TextParserBase(String8("CpuFreqParser")) {};
|
||||
~CpuFreqParser() {};
|
||||
|
||||
virtual status_t Parse(const int in, const int out) const;
|
||||
};
|
||||
|
||||
#endif // CPU_FREQ_PARSER_H
|
6
cmds/incident_helper/testdata/cpufreq.txt
vendored
Normal file
6
cmds/incident_helper/testdata/cpufreq.txt
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
freq cpu0 cpu1 cpu2 cpu3
|
||||
307200 23860761 23860761 23890935 23890935
|
||||
384000 83124 83124 29383 29383
|
||||
748800 N/A N/A 10547 10547
|
||||
768000 22652 22652 N/A N/A
|
||||
825600 N/A N/A 13173 13173
|
130
cmds/incident_helper/tests/CpuFreqParser_test.cpp
Normal file
130
cmds/incident_helper/tests/CpuFreqParser_test.cpp
Normal file
@ -0,0 +1,130 @@
|
||||
/*
|
||||
* Copyright (C) 2017 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "CpuFreqParser.h"
|
||||
|
||||
#include "frameworks/base/core/proto/android/os/cpufreq.pb.h"
|
||||
|
||||
#include <android-base/file.h>
|
||||
#include <android-base/test_utils.h>
|
||||
#include <gmock/gmock.h>
|
||||
#include <google/protobuf/message.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
using namespace android::base;
|
||||
using namespace android::os;
|
||||
using namespace std;
|
||||
using ::testing::StrEq;
|
||||
using ::testing::Test;
|
||||
using ::testing::internal::CaptureStderr;
|
||||
using ::testing::internal::CaptureStdout;
|
||||
using ::testing::internal::GetCapturedStderr;
|
||||
using ::testing::internal::GetCapturedStdout;
|
||||
|
||||
class CpuFreqParserTest : public Test {
|
||||
public:
|
||||
virtual void SetUp() override {
|
||||
ASSERT_TRUE(tf.fd != -1);
|
||||
}
|
||||
|
||||
string getSerializedString(::google::protobuf::Message& message) {
|
||||
string expectedStr;
|
||||
message.SerializeToFileDescriptor(tf.fd);
|
||||
ReadFileToString(tf.path, &expectedStr);
|
||||
return expectedStr;
|
||||
}
|
||||
|
||||
protected:
|
||||
TemporaryFile tf;
|
||||
|
||||
const string kTestPath = GetExecutableDirectory();
|
||||
const string kTestDataPath = kTestPath + "/testdata/";
|
||||
};
|
||||
|
||||
TEST_F(CpuFreqParserTest, Success) {
|
||||
const string testFile = kTestDataPath + "cpufreq.txt";
|
||||
CpuFreqParser parser;
|
||||
CpuFreq expected;
|
||||
|
||||
long jiffyHz = sysconf(_SC_CLK_TCK);
|
||||
expected.set_jiffy_hz(jiffyHz);
|
||||
|
||||
CpuFreqStats::TimeInState* state;
|
||||
|
||||
CpuFreqStats* cpu0 = expected.add_cpu_freqs();
|
||||
cpu0->set_cpu_name("cpu0");
|
||||
state = cpu0->add_times();
|
||||
state->set_state_khz(307200);
|
||||
state->set_time_jiffy(23860761);
|
||||
state = cpu0->add_times();
|
||||
state->set_state_khz(384000);
|
||||
state->set_time_jiffy(83124);
|
||||
state = cpu0->add_times();
|
||||
state->set_state_khz(768000);
|
||||
state->set_time_jiffy(22652);
|
||||
|
||||
CpuFreqStats* cpu1 = expected.add_cpu_freqs();
|
||||
cpu1->set_cpu_name("cpu1");
|
||||
state = cpu1->add_times();
|
||||
state->set_state_khz(307200);
|
||||
state->set_time_jiffy(23860761);
|
||||
state = cpu1->add_times();
|
||||
state->set_state_khz(384000);
|
||||
state->set_time_jiffy(83124);
|
||||
state = cpu1->add_times();
|
||||
state->set_state_khz(768000);
|
||||
state->set_time_jiffy(22652);
|
||||
|
||||
CpuFreqStats* cpu2 = expected.add_cpu_freqs();
|
||||
cpu2->set_cpu_name("cpu2");
|
||||
state = cpu2->add_times();
|
||||
state->set_state_khz(307200);
|
||||
state->set_time_jiffy(23890935);
|
||||
state = cpu2->add_times();
|
||||
state->set_state_khz(384000);
|
||||
state->set_time_jiffy(29383);
|
||||
state = cpu2->add_times();
|
||||
state->set_state_khz(748800);
|
||||
state->set_time_jiffy(10547);
|
||||
state = cpu2->add_times();
|
||||
state->set_state_khz(825600);
|
||||
state->set_time_jiffy(13173);
|
||||
|
||||
CpuFreqStats* cpu3 = expected.add_cpu_freqs();
|
||||
cpu3->set_cpu_name("cpu3");
|
||||
state = cpu3->add_times();
|
||||
state->set_state_khz(307200);
|
||||
state->set_time_jiffy(23890935);
|
||||
state = cpu3->add_times();
|
||||
state->set_state_khz(384000);
|
||||
state->set_time_jiffy(29383);
|
||||
state = cpu3->add_times();
|
||||
state->set_state_khz(748800);
|
||||
state->set_time_jiffy(10547);
|
||||
state = cpu3->add_times();
|
||||
state->set_state_khz(825600);
|
||||
state->set_time_jiffy(13173);
|
||||
|
||||
int fd = open(testFile.c_str(), O_RDONLY);
|
||||
ASSERT_TRUE(fd != -1);
|
||||
|
||||
CaptureStdout();
|
||||
ASSERT_EQ(NO_ERROR, parser.Parse(fd, STDOUT_FILENO));
|
||||
EXPECT_EQ(GetCapturedStdout(), getSerializedString(expected));
|
||||
close(fd);
|
||||
}
|
@ -56,7 +56,7 @@ protected:
|
||||
const string kTestDataPath = kTestPath + "/testdata/";
|
||||
};
|
||||
|
||||
TEST_F(CpuInfoParserTest, HasSwapInfo) {
|
||||
TEST_F(CpuInfoParserTest, Success) {
|
||||
const string testFile = kTestDataPath + "cpuinfo.txt";
|
||||
CpuInfoParser parser;
|
||||
CpuInfo expected;
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include <unistd.h>
|
||||
#include <wait.h>
|
||||
|
||||
const bool DEBUG = false;
|
||||
const ssize_t BUFFER_SIZE = 16 * 1024; // 16 KB
|
||||
const ssize_t MAX_BUFFER_COUNT = 256; // 4 MB max
|
||||
|
||||
@ -71,9 +72,11 @@ FdBuffer::read(int fd, int64_t timeout)
|
||||
mTimedOut = true;
|
||||
break;
|
||||
} else if (count < 0) {
|
||||
if (DEBUG) ALOGD("poll failed: %s", strerror(errno));
|
||||
return -errno;
|
||||
} else {
|
||||
if ((pfds.revents & POLLERR) != 0) {
|
||||
if (DEBUG) ALOGD("return event has error %s", strerror(errno));
|
||||
return errno != 0 ? -errno : UNKNOWN_ERROR;
|
||||
} else {
|
||||
ssize_t amt = ::read(fd, mBuffer.writeBuffer(), mBuffer.currentToWrite());
|
||||
@ -81,6 +84,7 @@ FdBuffer::read(int fd, int64_t timeout)
|
||||
if (errno == EAGAIN || errno == EWOULDBLOCK) {
|
||||
continue;
|
||||
} else {
|
||||
if (DEBUG) ALOGD("Fail to read %d: %s", fd, strerror(errno));
|
||||
return -errno;
|
||||
}
|
||||
} else if (amt == 0) {
|
||||
@ -95,7 +99,7 @@ FdBuffer::read(int fd, int64_t timeout)
|
||||
}
|
||||
|
||||
status_t
|
||||
FdBuffer::readProcessedDataInStream(int fd, int toFd, int fromFd, int64_t timeoutMs)
|
||||
FdBuffer::readProcessedDataInStream(int fd, int toFd, int fromFd, int64_t timeoutMs, const bool isSysfs)
|
||||
{
|
||||
struct pollfd pfds[] = {
|
||||
{ .fd = fd, .events = POLLIN },
|
||||
@ -135,12 +139,18 @@ FdBuffer::readProcessedDataInStream(int fd, int toFd, int fromFd, int64_t timeou
|
||||
mTimedOut = true;
|
||||
break;
|
||||
} else if (count < 0) {
|
||||
if (DEBUG) ALOGD("Fail to poll: %s", strerror(errno));
|
||||
return -errno;
|
||||
}
|
||||
|
||||
// make sure no errors occur on any fds
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
if ((pfds[i].revents & POLLERR) != 0) {
|
||||
if (i == 0 && isSysfs) {
|
||||
if (DEBUG) ALOGD("fd %d is sysfs, ignore its POLLERR return value", fd);
|
||||
continue;
|
||||
}
|
||||
if (DEBUG) ALOGD("fd[%d]=%d returns error events: %s", i, fd, strerror(errno));
|
||||
return errno != 0 ? -errno : UNKNOWN_ERROR;
|
||||
}
|
||||
}
|
||||
@ -155,6 +165,7 @@ FdBuffer::readProcessedDataInStream(int fd, int toFd, int fromFd, int64_t timeou
|
||||
}
|
||||
if (amt < 0) {
|
||||
if (!(errno == EAGAIN || errno == EWOULDBLOCK)) {
|
||||
if (DEBUG) ALOGD("Fail to read fd %d: %s", fd, strerror(errno));
|
||||
return -errno;
|
||||
} // otherwise just continue
|
||||
} else if (amt == 0) { // reach EOF so don't have to poll pfds[0].
|
||||
@ -176,6 +187,7 @@ FdBuffer::readProcessedDataInStream(int fd, int toFd, int fromFd, int64_t timeou
|
||||
}
|
||||
if (amt < 0) {
|
||||
if (!(errno == EAGAIN || errno == EWOULDBLOCK)) {
|
||||
if (DEBUG) ALOGD("Fail to write toFd %d: %s", toFd, strerror(errno));
|
||||
return -errno;
|
||||
} // otherwise just continue
|
||||
} else {
|
||||
@ -202,6 +214,7 @@ FdBuffer::readProcessedDataInStream(int fd, int toFd, int fromFd, int64_t timeou
|
||||
ssize_t amt = ::read(fromFd, mBuffer.writeBuffer(), mBuffer.currentToWrite());
|
||||
if (amt < 0) {
|
||||
if (!(errno == EAGAIN || errno == EWOULDBLOCK)) {
|
||||
if (DEBUG) ALOGD("Fail to read fromFd %d: %s", fromFd, strerror(errno));
|
||||
return -errno;
|
||||
} // otherwise just continue
|
||||
} else if (amt == 0) {
|
||||
|
@ -47,8 +47,10 @@ public:
|
||||
* and stores the processed data from 'fromFd' in memory for later usage.
|
||||
* This function behaves in a streaming fashion in order to save memory usage.
|
||||
* Returns NO_ERROR if there were no errors or if we timed out.
|
||||
*
|
||||
* Poll will return POLLERR if fd is from sysfs, handle this edge case.
|
||||
*/
|
||||
status_t readProcessedDataInStream(int fd, int toFd, int fromFd, int64_t timeoutMs);
|
||||
status_t readProcessedDataInStream(int fd, int toFd, int fromFd, int64_t timeoutMs, const bool isSysfs=false);
|
||||
|
||||
/**
|
||||
* Whether we timed out.
|
||||
|
@ -232,6 +232,7 @@ FileSection::FileSection(int id, const char* filename, const int64_t timeoutMs)
|
||||
mFilename(filename)
|
||||
{
|
||||
name = filename;
|
||||
mIsSysfs = strncmp(filename, "/sys/", 5) == 0;
|
||||
}
|
||||
|
||||
FileSection::~FileSection() {}
|
||||
@ -264,7 +265,7 @@ FileSection::Execute(ReportRequestSet* requests) const
|
||||
|
||||
// parent process
|
||||
status_t readStatus = buffer.readProcessedDataInStream(fd, p2cPipe.writeFd(), c2pPipe.readFd(),
|
||||
this->timeoutMs);
|
||||
this->timeoutMs, mIsSysfs);
|
||||
if (readStatus != NO_ERROR || buffer.timedOut()) {
|
||||
ALOGW("FileSection '%s' failed to read data from incident helper: %s, timedout: %s, kill: %s",
|
||||
this->name.string(), strerror(-readStatus), buffer.timedOut() ? "true" : "false",
|
||||
|
@ -69,6 +69,7 @@ public:
|
||||
|
||||
private:
|
||||
const char* mFilename;
|
||||
bool mIsSysfs; // sysfs files are pollable but return POLLERR by default, handle it separately
|
||||
};
|
||||
|
||||
/**
|
||||
|
41
core/proto/android/os/cpufreq.proto
Normal file
41
core/proto/android/os/cpufreq.proto
Normal file
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright (C) 2016 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
syntax = "proto2";
|
||||
|
||||
option java_multiple_files = true;
|
||||
option java_outer_classname = "CpuFreqProto";
|
||||
|
||||
package android.os;
|
||||
|
||||
// cpu frequency time from /sys/devices/system/cpu/cpufreq/all_time_in_state
|
||||
message CpuFreq {
|
||||
|
||||
optional int32 jiffy_hz = 1; // obtain by system config.
|
||||
|
||||
repeated CpuFreqStats cpu_freqs = 2;
|
||||
}
|
||||
|
||||
// frequency time pre cpu, unit in jiffy, TODO: obtain jiffies.
|
||||
message CpuFreqStats {
|
||||
|
||||
optional string cpu_name = 1;
|
||||
|
||||
message TimeInState {
|
||||
optional int32 state_khz = 1; // cpu frequency
|
||||
optional int64 time_jiffy = 2; // number of jiffies
|
||||
}
|
||||
repeated TimeInState times = 2;
|
||||
}
|
@ -20,6 +20,7 @@ option java_outer_classname = "IncidentProtoMetadata";
|
||||
|
||||
import "frameworks/base/libs/incident/proto/android/privacy.proto";
|
||||
import "frameworks/base/libs/incident/proto/android/section.proto";
|
||||
import "frameworks/base/core/proto/android/os/cpufreq.proto";
|
||||
import "frameworks/base/core/proto/android/os/cpuinfo.proto";
|
||||
import "frameworks/base/core/proto/android/os/incidentheader.proto";
|
||||
import "frameworks/base/core/proto/android/os/kernelwake.proto";
|
||||
@ -74,6 +75,10 @@ message IncidentProto {
|
||||
(section).args = "/system/bin/top -b -n 1 -H -s 6 -o pid,tid,user,pr,ni,%cpu,s,virt,res,pcy,cmd,name"
|
||||
];
|
||||
|
||||
optional CpuFreq cpu_freq = 2004 [
|
||||
(section).type = SECTION_FILE,
|
||||
(section).args = "/sys/devices/system/cpu/cpufreq/all_time_in_state"
|
||||
];
|
||||
|
||||
// System Services
|
||||
optional com.android.server.fingerprint.FingerprintServiceDumpProto fingerprint = 3000 [
|
||||
|
Loading…
x
Reference in New Issue
Block a user