syntax = "proto3";

package twitch.video.autoprof.profiler;
option go_package = "code.justin.tv/video/autoprof-tools/profiler";

// Structure and comments are from profiler.amazon.com's docs, available at
// https://profiler-docs.aka.amazon.com/documentation/design/protocol.html

// A profile is an IonStruct with the following components.
message Profile {

  // required. name of the application being profiled (to be replaced by appId
  // once we have app registration)
  string app_name = 1 [json_name="appName"];

  // required. start time of the profile in ms since UTC epoch
  int64 start_time_millis = 2 [json_name="start"];

  // required. end time of the profile in ms since UTC epoch
  int64 end_time_millis = 3 [json_name="end"];

  // required. callgraph of the profile.
  CallgraphNode callgraph = 4 [json_name="callgraphNode"];

  // metadata related to the profile.
  Metadata metadata = 5 [json_name="metadata"];

  // metrics related to the profile.
  Metrics metrics = 6 [json_name="metrics"];

  // internal use only. agent specific metadata about the profile.
  AgentMetadata = 7 [json_name="agentMetadata"];

}

// CallGraph is a tree formed by combining the sampled call stacks (bottom to
// top). callgraph in the profile struct points to the root node of this tree.
// Root node is a special node which acts as the root for combining the call
// stacks and does not map to a frame or have any self-time counts.
//
// A callgraphNode is a recursive IonStruct with counts and children.
message CallgraphNode {

  // counts is an IonStruct mapping counter names to the numeric counts. A
  // numeric count represents the number of times a node was sampled for the
  // particular counter (in the example above, the call graph node mapped by
  // frame 'foo' has RUNNABLE count of 5 and BLOCKED count of 3).
  //
  // Two types of counts are supported by the schema
  //
  //   - Accumulated Counts: This counts all the frames of the sampled call
  //     stack.
  //
  //   - Self-time Counts: This counts only the top of the sampled call stack.
  //     Recursively adding children's self-time counts with the node's self-
  //     time counts equals the accumulated counts.
  //
  // When using self-time counts, non-leaf callgraphNodes may not have counts,
  // and the root node has no counts. Counts being absent/empty for root node
  // is used as the check to determine if the call graph has self-time counts.
  // Note that the backend accepts profiles from agents with any type of
  // counts and accumulates counts as part of normalization of the agent
  // profiles.
  map<string, int32> counts = 1 [json_name="counts"];

  // children is an IonStruct mapping frame names to callgraphNodes.
  map<string, CallgraphNode> children = 2 [json_name="children"];

}

// Metadata is an IonStruct with contextual information about the profile.
// Each metadata field has a clearly defined use in a feature(like
// normalization or cost calculation), and has defined merge
// characteristics(how should it be handled during aggregation). Agents are
// required to send in the metadata fields for the dependent feature to work.
// Any metadata that does not fit the criteria and is only needed for
// tracking/debugging purposes should go into agent metadata.
//
// Following are the supported metadata fields. Unsupported metadata fields
// are dropped during normalization of agent profiles.
message Metadata {

  // The sampling interval used for capturing of the data in millisecond. As
  // part of normalization, the counts are scaled to a factor of default
  // sampling interval(1 second) / samplingInterval. This field will not be
  // present in the normalized profiles and absence of this field in agent
  // profiles will mean that the sampling was done with the default sampling
  // interval.
  int sample_interval_millis = 1 [json_name="samplingInterval"];

  // The sample weight of each sample in the profile based on the number of
  // hosts being sampled. This is currently the inverse of startupProbability
  // of the host in our java agent. As part of normalization, the counts are
  // scaled by this factor. This field will not be present in the normalized
  // profiles and absence of this field in profiles will mean that the
  // hostWeight is 1.
  int host_weight = 2 [json_name="hostWeight"];

  // The sample weight of each sample in the profile due to thread sampling.
  // This is number of total threads / number of threads sampled . As part of
  // normalization, the counts are scaled by this factor. This field will not
  // be present in the normalized profiles and absence of this field in
  // profiles will mean that the threadWeight is 1.
  float64 thread_weight = 3 [json_name="threadWeight"];

  // Mapping between the instance types and the number of such instances.
  // Example fleetInfo: { m4.xlarge: 4.0 }. This is currently used for cost
  // calculation and Agent is expected to send this information for cost
  // calculation to show on the UI.
  map<string, float64> fleet_info = 4 [json_name="fleetInfo"];

  // The number of samples in a given profile. This is the same the sum of
  // counts on the root node in callgraph in an unscaled profile, but since we
  // scale the profile based on weights, this information is lost. This is not
  // expected from the agent profile as it is inferred from the callgraph.
  int sample_count = 5 [json_name="numSamples"];

}

// Metrics is an IonStruct used to track metrics about the application. These
// are optional for now but could be made required as they get incorporated
// into features. Similar to metadata, each metric should have a clearly
// defined use/potential use and any metrics used for debugging belong in the
// agent metadata.
//
// All the metrics will be of type decimal and will be merged additively pro-
// rated by the proportion of the time window that the profile being merged
// overlaps. The average value across the profiles for a metric can be
// determined by dividing by numProfilesMerged.
//
// Following are the supported metrics. Unsupported metrics are dropped during
// aggregation.
message Metrics {

  // The sum of proportion of profiles merged to create the current profile.
  // Other metrics can be divided by this to find their average. This is not
  // expected to be present in the agent profile(defaults to 1).
  int32 merged_profile_count = 1 [json_name="numProfilesMerged"];

  // Average cpu usage in percentage by the profiled process across the time-
  // frame of the profile. Unused right now.
  float64 cpu_usage_percent = 2 [json_name="cpuUsage"];

  // Average memory usage in bytes by the profiler agent across the time-frame
  // of the profile. This could be used to show the memory overhead of the
  // agent on the user's application.
  int64 profiler_memory_usage_bytes = 3 [json_name="profilerMemUsage"];

  // Average number of threads in the application across the time-frame of the
  // profile. This is useful for statistical significance calculations.
  int32 thread_count = 4 [json_name="numThreads"];

}

// For the agents built by us, we want to pass around extra metadata/metrics
// about the agent for tracking and debugging purposes. This is for internal
// purpose only and not exposed externally.
//
// The type of each of the agent metadata fields could be any valid Ion type.
// This IonStruct is converted to JSON and logged as is (by inserting the
// profile name, start and end) and will be setup to be searchable through
// kibana for debugging. This will be removed from the profile during
// aggregation.
//
// Few common metadata fields are listed below for agents to support (note
// that all the agent metadata will be logged and any field can be added
// without updating the backend. The list below is to keep this consistent
// across agents). It is also recommened to add metrics about the performance
// of the agent.
message AgentMetadata {

  // Type of the agent (eg. java).
  string agent_type = 1 [json_name="agentType"];

  // The name of the host profile.
  string host_name = 2 [json_name="hostName"];

  // Flags signifying exception conditions during processing.
  repeated string flags = 3 [json_name="flags"];

  // AWS account number of the profiled host. Useful for BI.
  string aws_account = 4 [json_name="awsAccount"];

  // AWS account number of the profiled host. Useful for BI.
  string aws_region = 5 [json_name="awsRegion"];

}
