blob: a6ef651bc57a37bfd134fea46969d273f21fd7fa [file] [log] [blame]
// Copyright 2017 The Cobalt Authors. All Rights Reserved.
//
// 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 COBALT_BROWSER_MEMORY_TRACKER_TOOL_UTIL_H_
#define COBALT_BROWSER_MEMORY_TRACKER_TOOL_UTIL_H_
#include <map>
#include <sstream>
#include <string>
#include <vector>
#include "base/callback.h"
#include "base/logging.h"
#include "base/time/time.h"
#include "starboard/common/string.h"
#include "starboard/types.h"
namespace cobalt {
namespace browser {
namespace memory_tracker {
// Used for CSV generation.
extern const char kQuote[];
extern const char kDelimiter[];
extern const char kNewLine[];
// This is a simple algorithm to remove the "needle" from the haystack. Note
// that this function is simple and not well optimized.
std::string RemoveString(const std::string& haystack, const char* needle);
// Not optimized but works ok for a tool that dumps out in user time.
std::string SanitizeCSVKey(std::string key);
// Converts "2345.54" => "2,345.54".
std::string InsertCommasIntoNumberString(const std::string& input);
template <typename T>
std::string NumberFormatWithCommas(T val) {
// Convert value to string.
std::stringstream ss;
ss << val;
std::string s = InsertCommasIntoNumberString(ss.str());
return s;
}
// Removes odd elements and resizes vector.
template <typename VectorType>
void RemoveOddElements(VectorType* v) {
typedef typename VectorType::iterator iterator;
iterator read_it = v->end();
iterator write_it = v->end();
for (size_t i = 0; i * 2 < v->size(); ++i) {
write_it = v->begin() + i;
read_it = v->begin() + (i * 2);
*write_it = *read_it;
}
if (write_it != v->end()) {
write_it++;
}
v->erase(write_it, v->end());
}
// Simple timer class that fires periodically after time has elapsed.
class Timer {
public:
typedef base::Callback<base::TimeTicks(void)> TimeFunctor;
explicit Timer(base::TimeDelta delta_time);
Timer(base::TimeDelta delta_time, TimeFunctor f);
void Restart();
bool UpdateAndIsExpired(); // Returns true if the expiration was triggered.
void ScaleTriggerTime(double scale);
private:
base::TimeTicks Now();
base::TimeTicks start_time_;
base::TimeDelta time_before_expiration_;
TimeFunctor time_function_override_;
};
struct AllocationSamples {
std::vector<int32_t> number_allocations_;
std::vector<int64_t> allocated_bytes_;
};
typedef std::map<std::string, AllocationSamples> MapAllocationSamples;
typedef std::vector<base::TimeDelta> TimeStamps;
struct TimeSeries {
MapAllocationSamples samples_;
TimeStamps time_stamps_;
};
// Generates a linear fit in the form of slope / y-intercept form.
// Returns true if linear fit was calculated. False otherwise. Reasons for
// returning false include passing in an empty range, such that
// begin_it == end_it, or all x-values being the same.
//
// Algorithm adapted from:
// https://en.wikipedia.org/wiki/Simple_linear_regression#Fitting_the_regression_line
// Example:
// std::vector<std::pair<int, int> > data;
// for (int i = 0; i < 10; ++i) {
// data.push_back(std::pair<int, int>(i+1, 2*i));
// }
// double slope = 0;
// double y_intercept = 0;
// GetLinearFit(data.begin(), data.end(), &slope, &y_intercept);
// std::cout << "slope: " << slope << "\n";
// std::cout << "y_intercept: " << y_intercept<< "\n";
template <typename PairIterator>
bool GetLinearFit(PairIterator begin_it, PairIterator end_it, double* out_slope,
double* out_yintercept) {
if (begin_it == end_it) {
return false;
}
size_t n = 0;
double x_avg = 0;
double y_avg = 0;
for (PairIterator it = begin_it; it != end_it; ++it) {
x_avg += it->first;
y_avg += it->second;
n++;
}
x_avg /= n;
y_avg /= n;
double numerator = 0;
double denominator = 0;
for (PairIterator it = begin_it; it != end_it; ++it) {
double x_variance = it->first - x_avg;
double y_variance = it->second - y_avg;
numerator += (x_variance * y_variance);
denominator += (x_variance * x_variance);
}
if (denominator == 0.0) {
return false; // Failed to generate any value.
}
double slope = numerator / denominator;
double yintercept = y_avg - slope * x_avg;
*out_slope = slope;
*out_yintercept = yintercept;
return true;
}
// Segment represents a named memory segment of allocated memory.
// The name is optional and can be null.
class Segment {
public:
// Equal name string values must have equal pointers.
Segment(const std::string* name, const char* start_address,
const char* end_address);
// Using the page_size, split this Segment into one Segment
// per page. Each of the sub_segments will copy the name
// pointer from this.
void SplitAcrossPageBoundaries(size_t page_size,
std::vector<Segment>* sub_segments) const;
bool Intersects(const Segment& other) const;
bool operator<(const Segment& other) const;
bool operator==(const Segment& other) const;
size_t size() const;
const std::string* name() const { return name_; }
const char* begin() const { return begin_; }
const char* end() const { return end_; }
private:
const std::string* name_;
const char* begin_;
const char* end_;
};
// Returns a substring with the directory path removed from the filename.
// Example:
// F::BaseNameFast("directory/filename.cc") => "filename.cc"
// F::BaseNameFast("directory\filename.cc") => "filename.cc"
//
// Note that base::FilePath::BaseName() isn't used because of performance
// reasons.
const char* BaseNameFast(const char* file_name);
} // namespace memory_tracker
} // namespace browser
} // namespace cobalt
#endif // COBALT_BROWSER_MEMORY_TRACKER_TOOL_UTIL_H_