summaryrefslogtreecommitdiffstats
path: root/sandbox/linux/seccomp/timestats.cc
diff options
context:
space:
mode:
Diffstat (limited to 'sandbox/linux/seccomp/timestats.cc')
-rw-r--r--sandbox/linux/seccomp/timestats.cc191
1 files changed, 191 insertions, 0 deletions
diff --git a/sandbox/linux/seccomp/timestats.cc b/sandbox/linux/seccomp/timestats.cc
new file mode 100644
index 0000000..5d9b66a
--- /dev/null
+++ b/sandbox/linux/seccomp/timestats.cc
@@ -0,0 +1,191 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Helper program to analyze the time that Chrome's renderers spend in system
+// calls. Start Chrome like this:
+//
+// SECCOMP_SANDBOX_DEBUGGING=1 chrome --enable-seccomp-sandbox 2>&1 | timestats
+//
+// The program prints CPU time (0-100%) spent within system calls. This gives
+// a general idea of where it is worthwhile to spend effort optimizing Chrome.
+//
+// Caveats:
+// - there currently is no way to estimate what the overhead is for running
+// inside of the sandbox vs. running without a sandbox.
+// - we currently use a very simple heuristic to decide whether a system call
+// is blocking or not. Blocking system calls should not be included in the
+// computations. But it is quite possible for the numbers to be somewhat
+// wrong, because the heuristic failed.
+// - in order to collect this data, we have to turn on sandbox debugging.
+// There is a measurable performance penalty to doing so. Production numbers
+// are strictly better than the numbers reported by this tool.
+#include <set>
+#include <vector>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+#include <time.h>
+
+static const int kAvgWindowSizeMs = 500;
+static const int kPeakWindowSizeMs = 2*1000;
+
+// Class containing information on a single system call. Most notably, it
+// contains the time when the system call happened, and the time that it
+// took to complete.
+class Datum {
+ friend class Data;
+ public:
+ Datum(const char* name, double ms)
+ : name_(name),
+ ms_(ms) {
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ timestamp_ = tv.tv_sec*1000.0 + tv.tv_usec/1000.0;
+ }
+ virtual ~Datum() { }
+
+ double operator-(const Datum& b) {
+ return timestamp_ - b.timestamp_;
+ }
+
+ protected:
+ const char* name_;
+ double ms_;
+ double timestamp_;
+};
+
+// Class containing data on the most recent system calls. It maintains
+// sliding averages for total CPU time used, and it also maintains a peak
+// CPU usage. The peak usage is usually updated slower than the average
+// usage, as that makes it easier to inspect visually.
+class Data {
+ public:
+ Data() { }
+ virtual ~Data() { }
+
+ void addData(const char* name, double ms) {
+ average_.push_back(Datum(name, ms));
+ peak_.push_back(Datum(name, ms));
+
+ // Prune entries outside of the window
+ std::vector<Datum>::iterator iter;
+ for (iter = average_.begin();
+ *average_.rbegin() - *iter > kAvgWindowSizeMs;
+ ++iter) {
+ }
+ average_.erase(average_.begin(), iter);
+
+ for (iter = peak_.begin();
+ *peak_.rbegin() - *iter > kPeakWindowSizeMs;
+ ++iter){
+ }
+ peak_.erase(peak_.begin(), iter);
+
+ // Add the total usage of all system calls inside of the window
+ double total = 0;
+ for (iter = average_.begin(); iter != average_.end(); ++iter) {
+ total += iter->ms_;
+ }
+
+ // Compute the peak CPU usage during the last window
+ double peak = 0;
+ double max = 0;
+ std::vector<Datum>::iterator tail = peak_.begin();
+ for (iter = tail; iter != peak_.end(); ++iter) {
+ while (*iter - *tail > kAvgWindowSizeMs) {
+ peak -= tail->ms_;
+ ++tail;
+ }
+ peak += iter->ms_;
+ if (peak > max) {
+ max = peak;
+ }
+ }
+
+ // Print the average CPU usage in the last window
+ char buf[80];
+ total *= 100.0/kAvgWindowSizeMs;
+ max *= 100.0/kAvgWindowSizeMs;
+ sprintf(buf, "%6.2f%% (peak=%6.2f%%) ", total, max);
+
+ // Animate the actual usage, displaying both average and peak values
+ int len = strlen(buf);
+ int space = sizeof(buf) - len - 1;
+ int mark = (total * space + 50)/100;
+ int bar = (max * space + 50)/100;
+ for (int i = 0; i < mark; ++i) {
+ buf[len++] = '*';
+ }
+ if (mark == bar) {
+ if (bar) {
+ len--;
+ }
+ } else {
+ for (int i = 0; i < bar - mark - 1; ++i) {
+ buf[len++] = ' ';
+ }
+ }
+ buf[len++] = '|';
+ while (len < static_cast<int>(sizeof(buf))) {
+ buf[len++] = ' ';
+ }
+ strcpy(buf + len, "\r");
+ fwrite(buf, len + 1, 1, stdout);
+ fflush(stdout);
+ }
+
+ private:
+ std::vector<Datum> average_;
+ std::vector<Datum> peak_;
+};
+static Data data;
+
+
+int main(int argc, char *argv[]) {
+ char buf[80];
+ bool expensive = false;
+ while (fgets(buf, sizeof(buf), stdin)) {
+ // Allow longer delays for expensive system calls
+ if (strstr(buf, "This is an expensive system call")) {
+ expensive = true;
+ continue;
+ }
+
+ // Parse the string and extract the elapsed time
+ const char elapsed[] = "Elapsed time: ";
+ char* ms_string = strstr(buf, elapsed);
+ char* endptr;
+ double ms;
+ char* colon = strchr(buf, ':');
+
+ // If this string doesn't match, then it must be some other type of
+ // message. Just ignore it.
+ // It is quite likely that we will regularly encounter debug messages
+ // that either should be parsed by a completely different tool, or
+ // messages that were intended for humans to read.
+ if (!ms_string ||
+ ((ms = strtod(ms_string + sizeof(elapsed) - 1, &endptr)),
+ endptr == ms_string) ||
+ !colon) {
+ continue;
+ }
+
+ // Filter out system calls that were probably just blocking
+ // TODO(markus): automatically compute the cut-off for blocking calls
+ if (!expensive && ms > 0.05) {
+ continue;
+ }
+ expensive = false;
+
+ // Extract the name of the system call
+ *colon = '\000';
+
+ // Add the data point and update the display
+ data.addData(buf, ms);
+ }
+ puts("");
+ return 0;
+}