2018-09-14 15:04:28 +00:00
|
|
|
/**
|
|
|
|
* Copyright (c) 2014-present, Facebook, Inc.
|
|
|
|
* All rights reserved.
|
|
|
|
*
|
2019-01-21 19:48:49 +00:00
|
|
|
* This source code is licensed as defined on the LICENSE file found in the
|
|
|
|
* root directory of this source tree.
|
2018-09-14 15:04:28 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
#ifdef __linux__
|
|
|
|
// Needed for linux specific RUSAGE_THREAD, before including anything else
|
|
|
|
#ifndef _GNU_SOURCE
|
|
|
|
#define _GNU_SOURCE
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <cerrno>
|
|
|
|
#include <cstdint>
|
|
|
|
#include <cstring>
|
|
|
|
|
|
|
|
#include <sys/resource.h>
|
|
|
|
#include <sys/time.h>
|
|
|
|
|
|
|
|
#include <boost/format.hpp>
|
|
|
|
#include <boost/io/detail/quoted_manip.hpp>
|
|
|
|
|
|
|
|
#include <osquery/killswitch.h>
|
|
|
|
#include <osquery/logger.h>
|
|
|
|
#include <osquery/numeric_monitoring.h>
|
|
|
|
#include <osquery/profiler/profiler.h>
|
|
|
|
|
|
|
|
namespace osquery {
|
|
|
|
namespace {
|
|
|
|
|
2019-01-30 14:47:05 +00:00
|
|
|
void record(const std::vector<std::string>& names,
|
|
|
|
const std::string& metricName,
|
|
|
|
monitoring::ValueType measurement) {
|
|
|
|
for (const std::string& name : names) {
|
|
|
|
monitoring::record(name + "." + metricName,
|
|
|
|
measurement,
|
|
|
|
monitoring::PreAggregationType::None);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-14 15:04:28 +00:00
|
|
|
int getRusageWho() {
|
|
|
|
return
|
|
|
|
#ifdef __linux__
|
|
|
|
RUSAGE_THREAD; // Linux supports more granular profiling
|
|
|
|
#else
|
|
|
|
RUSAGE_SELF;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
enum class RusageError { FatalError = 1 };
|
|
|
|
|
|
|
|
static Expected<struct rusage, RusageError> callRusage() {
|
|
|
|
struct rusage stats;
|
|
|
|
const int who = getRusageWho();
|
|
|
|
auto rusage_status = getrusage(who, &stats);
|
|
|
|
if (rusage_status != -1) {
|
|
|
|
return stats;
|
|
|
|
} else {
|
|
|
|
return createError(RusageError::FatalError, "")
|
|
|
|
<< "Linux query profiling failed. error code: " << rusage_status
|
|
|
|
<< " message: " << boost::io::quoted(strerror(errno));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-30 14:47:05 +00:00
|
|
|
void recordRusageStatDifference(const std::vector<std::string>& names,
|
|
|
|
const std::string& stat_name,
|
|
|
|
int64_t start_stat,
|
|
|
|
int64_t end_stat) {
|
2018-09-14 15:04:28 +00:00
|
|
|
if (end_stat == 0) {
|
|
|
|
TLOG << "rusage field " << boost::io::quoted(stat_name)
|
|
|
|
<< " is not supported";
|
|
|
|
} else if (start_stat <= end_stat) {
|
2019-01-30 14:47:05 +00:00
|
|
|
record(names, stat_name, end_stat - start_stat);
|
2018-09-14 15:04:28 +00:00
|
|
|
} else {
|
|
|
|
LOG(WARNING) << "Possible overflow detected in rusage field: "
|
|
|
|
<< boost::io::quoted(stat_name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-30 14:47:05 +00:00
|
|
|
void recordRusageStatDifference(const std::vector<std::string>& names,
|
|
|
|
const std::string& stat_name,
|
|
|
|
const struct timeval& start_stat,
|
|
|
|
const struct timeval& end_stat) {
|
2018-09-14 15:04:28 +00:00
|
|
|
recordRusageStatDifference(
|
2019-01-30 14:47:05 +00:00
|
|
|
names,
|
|
|
|
stat_name + ".millis",
|
2018-09-14 15:04:28 +00:00
|
|
|
std::chrono::duration_cast<std::chrono::milliseconds>(
|
|
|
|
std::chrono::seconds(start_stat.tv_sec) +
|
|
|
|
std::chrono::microseconds(start_stat.tv_usec))
|
|
|
|
.count(),
|
|
|
|
std::chrono::duration_cast<std::chrono::milliseconds>(
|
|
|
|
std::chrono::seconds(end_stat.tv_sec) +
|
|
|
|
std::chrono::microseconds(end_stat.tv_usec))
|
2019-01-30 14:47:05 +00:00
|
|
|
.count());
|
2018-09-14 15:04:28 +00:00
|
|
|
}
|
|
|
|
|
2019-01-30 14:47:05 +00:00
|
|
|
void recordRusageStatDifference(const std::vector<std::string>& names,
|
|
|
|
const struct rusage& start_stats,
|
|
|
|
const struct rusage& end_stats) {
|
|
|
|
recordRusageStatDifference(names, "rss.max.kb", 0, end_stats.ru_maxrss);
|
2018-09-14 15:04:28 +00:00
|
|
|
|
2019-01-30 14:47:05 +00:00
|
|
|
recordRusageStatDifference(
|
|
|
|
names, "rss.increase.kb", start_stats.ru_maxrss, end_stats.ru_maxrss);
|
2018-09-14 15:04:28 +00:00
|
|
|
|
2019-01-30 14:47:05 +00:00
|
|
|
recordRusageStatDifference(
|
|
|
|
names, "input.load", start_stats.ru_inblock, end_stats.ru_inblock);
|
2018-09-14 15:04:28 +00:00
|
|
|
|
2019-01-30 14:47:05 +00:00
|
|
|
recordRusageStatDifference(
|
|
|
|
names, "output.load", start_stats.ru_oublock, end_stats.ru_oublock);
|
2018-09-14 15:04:28 +00:00
|
|
|
|
2019-01-30 14:47:05 +00:00
|
|
|
recordRusageStatDifference(
|
|
|
|
names, "time.user", start_stats.ru_utime, end_stats.ru_utime);
|
2018-09-14 15:04:28 +00:00
|
|
|
|
2019-01-30 14:47:05 +00:00
|
|
|
recordRusageStatDifference(
|
|
|
|
names, "time.system", start_stats.ru_stime, end_stats.ru_stime);
|
2018-09-14 15:04:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
class CodeProfiler::CodeProfilerData {
|
|
|
|
public:
|
|
|
|
CodeProfilerData()
|
|
|
|
: rusage_data_(callRusage()),
|
|
|
|
wall_time_(std::chrono::steady_clock::now()) {}
|
|
|
|
|
|
|
|
const std::chrono::time_point<std::chrono::steady_clock>& getWallTime() {
|
|
|
|
return wall_time_;
|
|
|
|
}
|
|
|
|
Expected<struct rusage, RusageError> takeRusageData() {
|
|
|
|
return std::move(rusage_data_);
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
Expected<struct rusage, RusageError> rusage_data_;
|
|
|
|
std::chrono::time_point<std::chrono::steady_clock> wall_time_;
|
|
|
|
};
|
|
|
|
|
2019-01-30 14:47:05 +00:00
|
|
|
CodeProfiler::CodeProfiler(const std::initializer_list<std::string>& names)
|
|
|
|
: names_(names), code_profiler_data_(new CodeProfilerData()) {}
|
2018-09-14 15:04:28 +00:00
|
|
|
|
|
|
|
CodeProfiler::~CodeProfiler() {
|
|
|
|
if (Killswitch::get().isPosixProfilingEnabled()) {
|
|
|
|
CodeProfilerData code_profiler_data_end;
|
|
|
|
|
|
|
|
auto rusage_start = code_profiler_data_->takeRusageData();
|
|
|
|
if (!rusage_start) {
|
|
|
|
LOG(ERROR) << "rusage_start error: "
|
|
|
|
<< rusage_start.getError().getFullMessageRecursive();
|
|
|
|
} else {
|
|
|
|
auto rusage_end = code_profiler_data_end.takeRusageData();
|
|
|
|
if (!rusage_end) {
|
|
|
|
LOG(ERROR) << "rusage_end error: "
|
|
|
|
<< rusage_end.getError().getFullMessageRecursive();
|
|
|
|
} else {
|
2019-01-30 14:47:05 +00:00
|
|
|
recordRusageStatDifference(names_, *rusage_start, *rusage_end);
|
2018-09-14 15:04:28 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const auto query_duration =
|
|
|
|
std::chrono::duration_cast<std::chrono::milliseconds>(
|
|
|
|
code_profiler_data_end.getWallTime() -
|
|
|
|
code_profiler_data_->getWallTime());
|
2019-01-30 14:47:05 +00:00
|
|
|
record(names_, "time.wall.millis", query_duration.count());
|
2018-09-14 15:04:28 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace osquery
|