blob: 21891aaa60fb5eb310d01cf1024358c2e6a4ede3 [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef GARNET_LIB_SYSTEM_MONITOR_DOCKYARD_DOCKYARD_H_
#define GARNET_LIB_SYSTEM_MONITOR_DOCKYARD_DOCKYARD_H_
#include <stdint.h>
#include <map>
#include <string>
#include <vector>
namespace dockyard {
// An integer value representing a sample stream name.
typedef uint32_t SampleStreamId;
// Sample time stamp in nanoseconds.
typedef uint64_t SampleTimeNs;
// The data type of a sample value.
typedef uint64_t SampleValue;
// This is not intended to remain a std::map. This works fine for small numbers
// of samples and it has the API desired. So a std::map is being used while
// framing out the API.
typedef std::map<SampleTimeNs, SampleValue> SampleStream;
// Special value for missing sample stream.
constexpr SampleValue NO_STREAM = (SampleValue)-1ULL;
// Special value for missing data.
constexpr SampleValue NO_DATA = (SampleValue)-2ULL;
// The highest value for sample data.
constexpr SampleValue SAMPLE_MAX_VALUE = (SampleValue)-3ULL;
// The upper value used to represent zero to one values with integers.
constexpr SampleValue NORMALIZATION_RANGE = 1000000ULL;
// A Sample.
struct Sample {
SampleTimeNs time;
// Sample values range from [0 to SAMPLE_MAX_VALUE].
SampleValue value;
};
// Mapping between IDs and name strings.
struct StreamInfo {
// The ID that corresponds to |name|, below.
SampleStreamId id;
// The name that corresponds to |id|, above.
std::string name;
};
struct StreamSetsRequest {
enum RenderStyle {
// When smoothing across samples, use a wider set of samples, including
// samples that are just outside of the sample set range. E.g. if the range
// is time 9 to 18, smooth over time 7 to 20.
WIDE_SMOOTHING,
// When sculpting across samples, pull the result toward the peaks and
// valleys in the data (rather than showing the average).
SCULPTING,
// For each column of the output, use the least value from the samples.
LOWEST_PER_COLUMN,
// For each column of the output, use the greatest value from the samples.
HIGHEST_PER_COLUMN,
// Add up the sample values for the slice of time and divide by the number
// of values found (i.e. take the average or mean).
AVERAGE_PER_COLUMN,
};
enum StreamSetsRequestFlags {
// Frame (or scale) the data set aesthetically. E.g. if the graph has little
// variance, zoom in to show that detail, rather then just having a flat
// vertical line in the graph. In some cases (like comparing graphs) this
// will be undesired. The values in the response will be in the range
// [0 to NORMALIZATION_RANGE].
NORMALIZE = 1 << 0,
};
StreamSetsRequest()
: request_id(0),
start_time_ns(0),
end_time_ns(0),
sample_count(0),
min(0),
max(0),
reserved(0),
render_style(AVERAGE_PER_COLUMN),
flags(0) {}
// For matching against a StreamSetsResponse::request_id.
uint64_t request_id;
// Request graph data for time range |start_time..end_time| that has
// |sample_count| values for each set. If the sample stream has more or less
// samples for that time range, virtual samples will be generated based on
// available samples.
SampleTimeNs start_time_ns;
SampleTimeNs end_time_ns;
uint64_t sample_count;
SampleValue min; // Future use.
SampleValue max; // Future use.
uint64_t reserved; // Future use.
RenderStyle render_style;
uint64_t flags;
// A StreamInfo::id for each stream of interest.
std::vector<SampleStreamId> stream_ids;
};
struct StreamSetsResponse {
// For matching against a StreamSetsRequest::request_id.
uint64_t request_id;
// The low and high all-time values for all sample streams requested. All-time
// means that these low and high points might not appear in the |data_sets|
// below. "All sample streams" means that these points may not appear in the
// same sample streams.
SampleValue lowest_value;
SampleValue highest_value;
// Each data set will correspond to a stream requested in the
// StreamSetsRequest::stream_ids. The value for each sample is normally in
// the range [0 to SAMPLE_MAX_VALUE]. If no value exists for the column, the
// value NO_DATA is used. If the StreamSetsRequest::stream_id was not found,
// the resulting sample will have the value NO_STREAM.
std::vector<std::vector<SampleValue>> data_sets;
};
// Lookup for a sample stream name string, given the sample stream ID.
typedef std::map<SampleStreamId, std::string> StreamInfoMap;
// Called when new streams are added or removed. Added values include their ID
// and string name. Removed values only have the ID.
// Intended to inform clients of StreamInfoMap changes (so they may keep their
// equivalent map in sync). The racy nature of this update is not an issue
// because the rest of the API will cope with invalid stream IDs, so 'eventually
// consistent' is acceptable).
// Use SetStreamNamesHandler() to install a StreamNamesCallback callback.
typedef std::function<void(const std::vector<StreamInfo>& add,
const std::vector<SampleStreamId>& remove)>
StreamNamesCallback;
// Called after (and in response to) a request is sent to |GetStreamSets()|.
// Use SetStreamSetsHandler() to install a StreamSetsCallback callback.
typedef std::function<void(const StreamSetsResponse& response)>
StreamSetsCallback;
class Dockyard {
public:
Dockyard();
~Dockyard();
// Insert sample information for a given stream_id. Not intended for use by
// the GUI.
void AddSamples(SampleStreamId stream_id, std::vector<Sample> samples);
// Get sample stream identifier for a given name. The ID values are stable
// throughout execution, so they may be cached.
//
// Returns an ID that corresponds to |name|.
SampleStreamId GetSampleStreamId(const std::string& name);
// Request graph data for time range |start_time..end_time| that has
// |sample_count| values for each set. If the sample stream has more or less
// samples for that time range, virtual samples will be generated based on
// available samples.
//
// The results will be supplied in a call to the |callback| previously set
// with SetStreamSetsHandler(). The |response| parameter on that callback will
// have the same context ID that is returned from this call to
// GetStreamSets() (i.e. that's how to match a response to a request).
//
// Returns unique context ID.
uint64_t GetStreamSets(StreamSetsRequest* request);
// Sets the function called when sample streams are added or removed. Pass
// nullptr as |callback| to stop receiving calls.
//
// Returns prior callback or nullptr.
StreamNamesCallback SetStreamNamesHandler(StreamNamesCallback callback);
// Sets the function called when sample stream data arrives in response to a
// call to GetStreamSets(). So, first set a handler with
// SetStreamSetsHandler(), then make as many GetStreamSets() calls as
// desired. Pass nullptr as |callback| to stop receiving calls.
//
// Returns prior callback or nullptr.
StreamSetsCallback SetStreamSetsHandler(StreamSetsCallback callback);
// Generate responses and call handlers for sample requests. Not intended for
// use by the GUI.
void ProcessRequests();
private:
typedef std::map<SampleStreamId, SampleStream*> SampleStreamMap;
uint64_t _next_context_id;
StreamNamesCallback _stream_name_handler;
StreamSetsCallback _stream_sets_handler;
std::vector<StreamSetsRequest*> _pending_requests;
SampleStreamMap _sample_streams;
std::map<SampleStreamId, std::pair<SampleValue, SampleValue>>
_sample_stream_low_high;
std::map<std::string, SampleStreamId> _stream_ids;
// Each of these Compute*() methods aggregate samples in different ways.
// There's no single 'true' way to represent aggregated data, so the choice
// is left to the caller. Which of these is used depends on the
// |StreamSetsRequestFlags| in the |StreamSetsRequest.flags| field.
void ComputeAveragePerColumn(SampleStreamId stream_id,
const SampleStream& sample_stream,
const StreamSetsRequest& request,
std::vector<SampleValue>* samples) const;
void ComputeHighestPerColumn(SampleStreamId stream_id,
const SampleStream& sample_stream,
const StreamSetsRequest& request,
std::vector<SampleValue>* samples) const;
void ComputeLowestPerColumn(SampleStreamId stream_id,
const SampleStream& sample_stream,
const StreamSetsRequest& request,
std::vector<SampleValue>* samples) const;
void ComputeSculpted(SampleStreamId stream_id,
const SampleStream& sample_stream,
const StreamSetsRequest& request,
std::vector<SampleValue>* samples) const;
void ComputeSmoothed(SampleStreamId stream_id,
const SampleStream& sample_stream,
const StreamSetsRequest& request,
std::vector<SampleValue>* samples) const;
// Rework the response so that all values are in the range 0 to one million.
// This represents a 0.0 to 1.0 value, scaled up.
void NormalizeResponse(SampleStreamId stream_id,
const SampleStream& sample_stream,
const StreamSetsRequest& request,
std::vector<SampleValue>* samples) const;
void ComputeLowestHighestForRequest(const StreamSetsRequest& request,
StreamSetsResponse* response) const;
// The average of the lowest and highest value in the stream.
SampleValue OverallAverageForStream(SampleStreamId stream_id) const;
// Gather the overall lowest and highest values encountered.
void ProcessSingleRequest(const StreamSetsRequest& request,
StreamSetsResponse* response) const;
};
} // namespace dockyard
#endif // GARNET_LIB_SYSTEM_MONITOR_DOCKYARD_DOCKYARD_H_