-
-
Notifications
You must be signed in to change notification settings - Fork 2.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Persist query performance stats #8250
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,3 +8,80 @@ | |
*/ | ||
|
||
#include "query_performance.h" | ||
#include "boost/lexical_cast.hpp" | ||
#include <boost/algorithm/string/classification.hpp> | ||
#include <boost/algorithm/string/split.hpp> | ||
#include <string> | ||
|
||
namespace osquery { | ||
|
||
// Helper function to convert a string to a given type. | ||
template <typename Result> | ||
Result convert(const std::string& source) { | ||
Result result; | ||
if (!boost::conversion::try_lexical_convert<Result>(source, result)) { | ||
return Result(); | ||
} | ||
return result; | ||
} | ||
|
||
QueryPerformance::QueryPerformance(const std::string& csv) { | ||
std::vector<std::string> parts; | ||
boost::split(parts, csv, boost::is_any_of(",")); | ||
// future proofing the size, in case additional fields are added | ||
if (parts.size() < 12) { | ||
return; | ||
} | ||
|
||
executions = convert<std::size_t>(parts[0]); | ||
last_executed = convert<std::uint64_t>(parts[1]); | ||
wall_time = convert<std::uint64_t>(parts[2]); | ||
wall_time_ms = convert<std::uint64_t>(parts[3]); | ||
last_wall_time_ms = convert<std::uint64_t>(parts[4]); | ||
user_time = convert<std::uint64_t>(parts[5]); | ||
last_user_time = convert<std::uint64_t>(parts[6]); | ||
system_time = convert<std::uint64_t>(parts[7]); | ||
last_system_time = convert<std::uint64_t>(parts[8]); | ||
average_memory = convert<std::uint64_t>(parts[9]); | ||
last_memory = convert<std::uint64_t>(parts[10]); | ||
output_size = convert<std::uint64_t>(parts[11]); | ||
} | ||
|
||
std::string QueryPerformance::toCSV() const { | ||
return std::to_string(executions) + "," + std::to_string(last_executed) + | ||
"," + std::to_string(wall_time) + "," + std::to_string(wall_time_ms) + | ||
"," + std::to_string(last_wall_time_ms) + "," + | ||
std::to_string(user_time) + "," + std::to_string(last_user_time) + | ||
"," + std::to_string(system_time) + "," + | ||
std::to_string(last_system_time) + "," + | ||
std::to_string(average_memory) + "," + std::to_string(last_memory) + | ||
"," + std::to_string(output_size); | ||
} | ||
Comment on lines
+28
to
+59
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. IIUC we are storing the performance data in the RocksDB store serialized to a CSV. I see the comment on line 31 about future proofing but I don't quite understand. Should we add more about what a future engineer would need to do to add/remove "columns" from the performance stats? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The comment refers to the validation check: If the check was: |
||
|
||
bool operator==(const QueryPerformance& l, const QueryPerformance& r) { | ||
return std::tie(l.executions, | ||
l.last_executed, | ||
l.wall_time, | ||
l.wall_time_ms, | ||
l.last_wall_time_ms, | ||
l.user_time, | ||
l.last_user_time, | ||
l.system_time, | ||
l.last_system_time, | ||
l.average_memory, | ||
l.last_memory, | ||
l.output_size) == std::tie(r.executions, | ||
r.last_executed, | ||
r.wall_time, | ||
r.wall_time_ms, | ||
r.last_wall_time_ms, | ||
r.user_time, | ||
r.last_user_time, | ||
r.system_time, | ||
r.last_system_time, | ||
r.average_memory, | ||
r.last_memory, | ||
r.output_size); | ||
} | ||
|
||
} // namespace osquery |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
/** | ||
* Copyright (c) 2014-present, The osquery authors | ||
* | ||
* This source code is licensed as defined by the LICENSE file found in the | ||
* root directory of this source tree. | ||
* | ||
* SPDX-License-Identifier: (Apache-2.0 OR GPL-2.0-only) | ||
*/ | ||
|
||
#include "osquery/core/sql/query_performance.h" | ||
#include <gtest/gtest.h> | ||
|
||
namespace osquery { | ||
|
||
class QueryPerformanceTests : public testing::Test {}; | ||
|
||
TEST_F(QueryPerformanceTests, test_query_performance) { | ||
// Default case | ||
QueryPerformance defaultStats; | ||
auto emptyStats = QueryPerformance(""); | ||
ASSERT_EQ(defaultStats, emptyStats); | ||
ASSERT_EQ("0,0,0,0,0,0,0,0,0,0,0,0", defaultStats.toCSV()); | ||
|
||
// Normal case | ||
{ | ||
QueryPerformance expected; | ||
expected.executions = 1; | ||
expected.last_executed = 2; | ||
expected.wall_time = 3; | ||
expected.wall_time_ms = 4; | ||
expected.last_wall_time_ms = 5; | ||
expected.user_time = 6; | ||
expected.last_user_time = 7; | ||
expected.system_time = 8; | ||
expected.last_system_time = 9; | ||
expected.average_memory = 10; | ||
expected.last_memory = 11; | ||
expected.output_size = 12; | ||
std::string csv = "1,2,3,4,5,6,7,8,9,10,11,12"; | ||
auto filledStats = QueryPerformance(csv); | ||
ASSERT_EQ(expected, filledStats); | ||
ASSERT_EQ(csv, expected.toCSV()); | ||
ASSERT_EQ(csv, filledStats.toCSV()); | ||
} | ||
|
||
// Invalid case | ||
{ | ||
std::string csv = "1,,bozo,4,5,6,7,8,9,10,11,12"; | ||
auto filledStats = QueryPerformance(csv); | ||
ASSERT_EQ(0, filledStats.last_executed); | ||
ASSERT_EQ(0, filledStats.wall_time); | ||
ASSERT_EQ("1,0,0,4,5,6,7,8,9,10,11,12", filledStats.toCSV()); | ||
} | ||
} | ||
|
||
} // namespace osquery |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My first thought, is that CSV feels like a brittle format. But looking at the data, it's a dozen numbers. And CSV doesn't feel wrong for that case. JSON feels like a lot of overhead, and ASN.1 is, well, ASN.1.
Though I'm a little leery of the parsing, but since I think rocksdb is all string values under the hood, I'm not sure there's better