mirror of
https://github.com/chromium/crashpad.git
synced 2025-01-15 10:07:56 +08:00
linux: Collect CPU times in ProcStatReader and use in ProcessReader
Bug: crashpad:30 Change-Id: I6d4020220031670937acad12d0b7878c1ae0fae7 Reviewed-on: https://chromium-review.googlesource.com/583952 Reviewed-by: Mark Mentovai <mark@chromium.org>
This commit is contained in:
parent
90e4649f0d
commit
01b347732e
@ -26,6 +26,7 @@
|
|||||||
#include "base/logging.h"
|
#include "base/logging.h"
|
||||||
#include "base/strings/string_number_conversions.h"
|
#include "base/strings/string_number_conversions.h"
|
||||||
#include "build/build_config.h"
|
#include "build/build_config.h"
|
||||||
|
#include "util/linux/proc_stat_reader.h"
|
||||||
#include "util/posix/scoped_dir.h"
|
#include "util/posix/scoped_dir.h"
|
||||||
|
|
||||||
namespace crashpad {
|
namespace crashpad {
|
||||||
@ -210,6 +211,46 @@ bool ProcessReader::Initialize(pid_t pid) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ProcessReader::StartTime(timeval* start_time) const {
|
||||||
|
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
||||||
|
return process_info_.StartTime(start_time);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ProcessReader::CPUTimes(timeval* user_time, timeval* system_time) const {
|
||||||
|
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
||||||
|
timerclear(user_time);
|
||||||
|
timerclear(system_time);
|
||||||
|
|
||||||
|
timeval local_user_time;
|
||||||
|
timerclear(&local_user_time);
|
||||||
|
timeval local_system_time;
|
||||||
|
timerclear(&local_system_time);
|
||||||
|
|
||||||
|
for (const Thread& thread : threads_) {
|
||||||
|
ProcStatReader stat;
|
||||||
|
if (!stat.Initialize(thread.tid)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
timeval thread_user_time;
|
||||||
|
if (!stat.UserCPUTime(&thread_user_time)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
timeval thread_system_time;
|
||||||
|
if (!stat.SystemCPUTime(&thread_system_time)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
timeradd(&local_user_time, &thread_user_time, &local_user_time);
|
||||||
|
timeradd(&local_system_time, &thread_system_time, &local_system_time);
|
||||||
|
}
|
||||||
|
|
||||||
|
*user_time = local_user_time;
|
||||||
|
*system_time = local_system_time;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
const std::vector<ProcessReader::Thread>& ProcessReader::Threads() {
|
const std::vector<ProcessReader::Thread>& ProcessReader::Threads() {
|
||||||
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
||||||
if (!initialized_threads_) {
|
if (!initialized_threads_) {
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
#ifndef CRASHPAD_SNAPSHOT_LINUX_PROCESS_READER_H_
|
#ifndef CRASHPAD_SNAPSHOT_LINUX_PROCESS_READER_H_
|
||||||
#define CRASHPAD_SNAPSHOT_LINUX_PROCESS_READER_H_
|
#define CRASHPAD_SNAPSHOT_LINUX_PROCESS_READER_H_
|
||||||
|
|
||||||
|
#include <sys/time.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
@ -83,6 +84,25 @@ class ProcessReader {
|
|||||||
//! \brief Return a memory map of the target process.
|
//! \brief Return a memory map of the target process.
|
||||||
MemoryMap* GetMemoryMap() { return &memory_map_; }
|
MemoryMap* GetMemoryMap() { return &memory_map_; }
|
||||||
|
|
||||||
|
//! \brief Determines the target process’ start time.
|
||||||
|
//!
|
||||||
|
//! \param[out] start_time The time that the process started.
|
||||||
|
//! \return `true` on success with \a start_time set. Otherwise `false` with a
|
||||||
|
//! message logged.
|
||||||
|
bool StartTime(timeval* start_time) const;
|
||||||
|
|
||||||
|
//! \brief Determines the target process’ execution time.
|
||||||
|
//!
|
||||||
|
//! \param[out] user_time The amount of time the process has executed code in
|
||||||
|
//! user mode.
|
||||||
|
//! \param[out] system_time The amount of time the process has executed code
|
||||||
|
//! in system mode.
|
||||||
|
//!
|
||||||
|
//! \return `true` on success, `false` on failure, with a warning logged. On
|
||||||
|
//! failure, \a user_time and \a system_time will be set to represent no
|
||||||
|
//! time spent executing code in user or system mode.
|
||||||
|
bool CPUTimes(timeval* user_time, timeval* system_time) const;
|
||||||
|
|
||||||
//! \brief Return a vector of threads that are in the task process. If the
|
//! \brief Return a vector of threads that are in the task process. If the
|
||||||
//! main thread is able to be identified and traced, it will be placed at
|
//! main thread is able to be identified and traced, it will be placed at
|
||||||
//! index `0`.
|
//! index `0`.
|
||||||
|
@ -43,67 +43,66 @@ void TimespecToTimeval(const timespec& ts, timeval* tv) {
|
|||||||
tv->tv_usec = ts.tv_nsec / 1000;
|
tv->tv_usec = ts.tv_nsec / 1000;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
long GetClockTicksPerSecond() {
|
||||||
|
long clock_ticks_per_s = sysconf(_SC_CLK_TCK);
|
||||||
|
if (clock_ticks_per_s <= 0) {
|
||||||
|
PLOG(ERROR) << "sysconf";
|
||||||
|
}
|
||||||
|
return clock_ticks_per_s;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
ProcStatReader::ProcStatReader() : tid_(-1) {}
|
ProcStatReader::ProcStatReader()
|
||||||
|
: contents_(), third_column_position_(0), initialized_() {}
|
||||||
|
|
||||||
ProcStatReader::~ProcStatReader() {}
|
ProcStatReader::~ProcStatReader() {}
|
||||||
|
|
||||||
bool ProcStatReader::Initialize(pid_t tid) {
|
bool ProcStatReader::Initialize(pid_t tid) {
|
||||||
INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
|
INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
|
||||||
// This might do more in the future.
|
if (!ReadFile(tid)) {
|
||||||
tid_ = tid;
|
|
||||||
INITIALIZATION_STATE_SET_VALID(initialized_);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ProcStatReader::StartTime(timeval* start_time) const {
|
|
||||||
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
|
||||||
std::string stat_contents;
|
|
||||||
if (!ReadFile(&stat_contents)) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// The process start time is the 22nd column.
|
// The first column is process ID and the second column is the executable name
|
||||||
// The second column is the executable name in parentheses.
|
// in parentheses. This class only cares about columns after the second, so
|
||||||
|
// find the start of the third here and save it for later.
|
||||||
// The executable name may have parentheses itself, so find the end of the
|
// The executable name may have parentheses itself, so find the end of the
|
||||||
// second column by working backwards to find the last closing parens and
|
// second column by working backwards to find the last closing parens.
|
||||||
// then count forward to the 22nd column.
|
size_t stat_pos = contents_.rfind(')');
|
||||||
size_t stat_pos = stat_contents.rfind(')');
|
|
||||||
if (stat_pos == std::string::npos) {
|
if (stat_pos == std::string::npos) {
|
||||||
LOG(ERROR) << "format error";
|
LOG(ERROR) << "format error";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int index = 1; index < 21; ++index) {
|
third_column_position_ = contents_.find(' ', stat_pos);
|
||||||
stat_pos = stat_contents.find(' ', stat_pos);
|
if (third_column_position_ == std::string::npos ||
|
||||||
if (stat_pos == std::string::npos) {
|
++third_column_position_ >= contents_.size()) {
|
||||||
break;
|
|
||||||
}
|
|
||||||
++stat_pos;
|
|
||||||
}
|
|
||||||
if (stat_pos >= stat_contents.size()) {
|
|
||||||
LOG(ERROR) << "format error";
|
LOG(ERROR) << "format error";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* ticks_ptr = &stat_contents[stat_pos];
|
INITIALIZATION_STATE_SET_VALID(initialized_);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ProcStatReader::UserCPUTime(timeval* user_time) const {
|
||||||
|
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
||||||
|
return ReadTimeAtIndex(13, user_time);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ProcStatReader::SystemCPUTime(timeval* system_time) const {
|
||||||
|
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
||||||
|
return ReadTimeAtIndex(14, system_time);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ProcStatReader::StartTime(timeval* start_time) const {
|
||||||
|
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
||||||
|
|
||||||
// start time is in jiffies instead of clock ticks pre 2.6.
|
|
||||||
uint64_t ticks_after_boot;
|
|
||||||
if (!AdvancePastNumber<uint64_t>(&ticks_ptr, &ticks_after_boot)) {
|
|
||||||
LOG(ERROR) << "format error";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
long clock_ticks_per_s = sysconf(_SC_CLK_TCK);
|
|
||||||
if (clock_ticks_per_s <= 0) {
|
|
||||||
PLOG(ERROR) << "sysconf";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
timeval time_after_boot;
|
timeval time_after_boot;
|
||||||
time_after_boot.tv_sec = ticks_after_boot / clock_ticks_per_s;
|
if (!ReadTimeAtIndex(21, &time_after_boot)) {
|
||||||
time_after_boot.tv_usec = (ticks_after_boot % clock_ticks_per_s) *
|
return false;
|
||||||
(static_cast<long>(1E6) / clock_ticks_per_s);
|
}
|
||||||
|
|
||||||
timespec uptime;
|
timespec uptime;
|
||||||
if (clock_gettime(CLOCK_BOOTTIME, &uptime) != 0) {
|
if (clock_gettime(CLOCK_BOOTTIME, &uptime) != 0) {
|
||||||
@ -126,13 +125,53 @@ bool ProcStatReader::StartTime(timeval* start_time) const {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ProcStatReader::ReadFile(std::string* contents) const {
|
bool ProcStatReader::ReadFile(pid_t tid) {
|
||||||
char path[32];
|
char path[32];
|
||||||
snprintf(path, arraysize(path), "/proc/%d/stat", tid_);
|
snprintf(path, arraysize(path), "/proc/%d/stat", tid);
|
||||||
if (!LoggingReadEntireFile(base::FilePath(path), contents)) {
|
if (!LoggingReadEntireFile(base::FilePath(path), &contents_)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ProcStatReader::FindColumn(int col_index, const char** column) const {
|
||||||
|
size_t position = third_column_position_;
|
||||||
|
for (int index = 2; index < col_index; ++index) {
|
||||||
|
position = contents_.find(' ', position);
|
||||||
|
if (position == std::string::npos) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
++position;
|
||||||
|
}
|
||||||
|
if (position >= contents_.size()) {
|
||||||
|
LOG(ERROR) << "format error";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
*column = &contents_[position];
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ProcStatReader::ReadTimeAtIndex(int index, timeval* time_val) const {
|
||||||
|
const char* ticks_ptr;
|
||||||
|
if (!FindColumn(index, &ticks_ptr)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t ticks;
|
||||||
|
if (!AdvancePastNumber<uint64_t>(&ticks_ptr, &ticks)) {
|
||||||
|
LOG(ERROR) << "format error";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static long clock_ticks_per_s = GetClockTicksPerSecond();
|
||||||
|
if (clock_ticks_per_s <= 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
time_val->tv_sec = ticks / clock_ticks_per_s;
|
||||||
|
time_val->tv_usec = (ticks % clock_ticks_per_s) *
|
||||||
|
(static_cast<long>(1E6) / clock_ticks_per_s);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace crashpad
|
} // namespace crashpad
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
#ifndef CRASHPAD_UTIL_LINUX_PROC_STAT_READER_H_
|
#ifndef CRASHPAD_UTIL_LINUX_PROC_STAT_READER_H_
|
||||||
#define CRASHPAD_UTIL_LINUX_PROC_STAT_READER_H_
|
#define CRASHPAD_UTIL_LINUX_PROC_STAT_READER_H_
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
|
||||||
@ -38,6 +39,22 @@ class ProcStatReader {
|
|||||||
//! \param[in] tid The thread ID to read the stat file for.
|
//! \param[in] tid The thread ID to read the stat file for.
|
||||||
bool Initialize(pid_t tid);
|
bool Initialize(pid_t tid);
|
||||||
|
|
||||||
|
//! \brief Determines the time the thread has spent executing in user mode.
|
||||||
|
//!
|
||||||
|
//! \param[out] user_time The time spent executing in user mode.
|
||||||
|
//!
|
||||||
|
//! \return `true` on success, with \a user_time set. Otherwise, `false` with
|
||||||
|
//! a message logged.
|
||||||
|
bool UserCPUTime(timeval* user_time) const;
|
||||||
|
|
||||||
|
//! \brief Determines the time the thread has spent executing in system mode.
|
||||||
|
//!
|
||||||
|
//! \param[out] system_time The time spent executing in system mode.
|
||||||
|
//!
|
||||||
|
//! \return `true` on success, with \a system_time set. Otherwise, `false`
|
||||||
|
//! with a message logged.
|
||||||
|
bool SystemCPUTime(timeval* system_time) const;
|
||||||
|
|
||||||
//! \brief Determines the target thread’s start time.
|
//! \brief Determines the target thread’s start time.
|
||||||
//!
|
//!
|
||||||
//! \param[out] start_time The time that the thread started.
|
//! \param[out] start_time The time that the thread started.
|
||||||
@ -47,9 +64,12 @@ class ProcStatReader {
|
|||||||
bool StartTime(timeval* start_time) const;
|
bool StartTime(timeval* start_time) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool ReadFile(std::string* contents) const;
|
bool ReadFile(pid_t tid);
|
||||||
|
bool FindColumn(int index, const char** column) const;
|
||||||
|
bool ReadTimeAtIndex(int index, timeval* time_val) const;
|
||||||
|
|
||||||
pid_t tid_;
|
std::string contents_;
|
||||||
|
size_t third_column_position_;
|
||||||
InitializationStateDcheck initialized_;
|
InitializationStateDcheck initialized_;
|
||||||
|
|
||||||
DISALLOW_COPY_AND_ASSIGN(ProcStatReader);
|
DISALLOW_COPY_AND_ASSIGN(ProcStatReader);
|
||||||
|
@ -36,6 +36,16 @@ TEST(ProcStatReader, Basic) {
|
|||||||
time_t now;
|
time_t now;
|
||||||
time(&now);
|
time(&now);
|
||||||
EXPECT_LE(start_time.tv_sec, now);
|
EXPECT_LE(start_time.tv_sec, now);
|
||||||
|
|
||||||
|
time_t elapsed_sec = now - start_time.tv_sec;
|
||||||
|
|
||||||
|
timeval user_time;
|
||||||
|
ASSERT_TRUE(stat.UserCPUTime(&user_time));
|
||||||
|
EXPECT_LE(user_time.tv_sec, elapsed_sec);
|
||||||
|
|
||||||
|
timeval system_time;
|
||||||
|
ASSERT_TRUE(stat.SystemCPUTime(&system_time));
|
||||||
|
EXPECT_LE(system_time.tv_sec, elapsed_sec);
|
||||||
}
|
}
|
||||||
|
|
||||||
pid_t gettid() {
|
pid_t gettid() {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user