/*
* Copyright (C) 2011 The Android Open Source Project
*
* 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 ART_RUNTIME_BASE_TIMING_LOGGER_H_
#define ART_RUNTIME_BASE_TIMING_LOGGER_H_
#include "base/histogram.h"
#include "base/macros.h"
#include "base/mutex.h"
#include <set>
#include <string>
#include <vector>
namespace art {
class TimingLogger;
class CumulativeLogger {
public:
explicit CumulativeLogger(const std::string& name);
~CumulativeLogger();
void Start();
void End() LOCKS_EXCLUDED(lock_);
void Reset() LOCKS_EXCLUDED(lock_);
void Dump(std::ostream& os) const LOCKS_EXCLUDED(lock_);
uint64_t GetTotalNs() const {
return GetTotalTime() * kAdjust;
}
// Allow the name to be modified, particularly when the cumulative logger is a field within a
// parent class that is unable to determine the "name" of a sub-class.
void SetName(const std::string& name) LOCKS_EXCLUDED(lock_);
void AddLogger(const TimingLogger& logger) LOCKS_EXCLUDED(lock_);
size_t GetIterations() const;
private:
class HistogramComparator {
public:
bool operator()(const Histogram<uint64_t>* a, const Histogram<uint64_t>* b) const {
return a->Name() < b->Name();
}
};
static constexpr size_t kLowMemoryBucketCount = 16;
static constexpr size_t kDefaultBucketCount = 100;
static constexpr size_t kInitialBucketSize = 50; // 50 microseconds.
void AddPair(const std::string &label, uint64_t delta_time)
EXCLUSIVE_LOCKS_REQUIRED(lock_);
void DumpHistogram(std::ostream &os) const EXCLUSIVE_LOCKS_REQUIRED(lock_);
uint64_t GetTotalTime() const {
return total_time_;
}
static const uint64_t kAdjust = 1000;
std::set<Histogram<uint64_t>*, HistogramComparator> histograms_ GUARDED_BY(lock_);
std::string name_;
const std::string lock_name_;
mutable Mutex lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
size_t iterations_ GUARDED_BY(lock_);
uint64_t total_time_;
DISALLOW_COPY_AND_ASSIGN(CumulativeLogger);
};
// A timing logger that knows when a split starts for the purposes of logging tools, like systrace.
class TimingLogger {
public:
static constexpr size_t kIndexNotFound = static_cast<size_t>(-1);
class Timing {
public:
Timing(uint64_t time, const char* name) : time_(time), name_(name) {
}
bool IsStartTiming() const {
return !IsEndTiming();
}
bool IsEndTiming() const {
return name_ == nullptr;
}
uint64_t GetTime() const {
return time_;
}
const char* GetName() const {
return name_;
}
private:
uint64_t time_;
const char* name_;
};
// Extra data that is only calculated when you call dump to prevent excess allocation.
class TimingData {
public:
TimingData() = default;
TimingData(TimingData&& other) {
std::swap(data_, other.data_);
}
TimingData& operator=(TimingData&& other) {
std::swap(data_, other.data_);
return *this;
}
uint64_t GetTotalTime(size_t idx) {
return data_[idx].total_time;
}
uint64_t GetExclusiveTime(size_t idx) {
return data_[idx].exclusive_time;
}
private:
// Each begin split has a total time and exclusive time. Exclusive time is total time - total
// time of children nodes.
struct CalculatedDataPoint {
CalculatedDataPoint() : total_time(0), exclusive_time(0) {}
uint64_t total_time;
uint64_t exclusive_time;
};
std::vector<CalculatedDataPoint> data_;
friend class TimingLogger;
};
explicit TimingLogger(const char* name, bool precise, bool verbose);
~TimingLogger();
// Verify that all open timings have related closed timings.
void Verify();
// Clears current timings and labels.
void Reset();
// Starts a timing.
void StartTiming(const char* new_split_label);
// Ends the current timing.
void EndTiming();
// End the current timing and start a new timing. Usage not recommended.
void NewTiming(const char* new_split_label) {
EndTiming();
StartTiming(new_split_label);
}
// Returns the total duration of the timings (sum of total times).
uint64_t GetTotalNs() const;
// Find the index of a timing by name.
size_t FindTimingIndex(const char* name, size_t start_idx) const;
void Dump(std::ostream& os, const char* indent_string = " ") const;
// Scoped timing splits that can be nested and composed with the explicit split
// starts and ends.
class ScopedTiming {
public:
explicit ScopedTiming(const char* label, TimingLogger* logger) : logger_(logger) {
logger_->StartTiming(label);
}
~ScopedTiming() {
logger_->EndTiming();
}
// Closes the current timing and opens a new timing.
void NewTiming(const char* label) {
logger_->NewTiming(label);
}
private:
TimingLogger* const logger_; // The timing logger which the scoped timing is associated with.
DISALLOW_COPY_AND_ASSIGN(ScopedTiming);
};
// Return the time points of when each start / end timings start and finish.
const std::vector<Timing>& GetTimings() const {
return timings_;
}
TimingData CalculateTimingData() const;
protected:
// The name of the timing logger.
const char* const name_;
// Do we want to print the exactly recorded split (true) or round down to the time unit being
// used (false).
const bool precise_;
// Verbose logging.
const bool verbose_;
// Timing points that are either start or end points. For each starting point ret[i] = location
// of end split associated with i. If it is and end split ret[i] = i.
std::vector<Timing> timings_;
private:
DISALLOW_COPY_AND_ASSIGN(TimingLogger);
};
} // namespace art
#endif // ART_RUNTIME_BASE_TIMING_LOGGER_H_