// Copyright (c) 2012 The Chromium OS 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 CHROMIUMOS_WIDE_PROFILING_PERF_SERIALIZER_H_
#define CHROMIUMOS_WIDE_PROFILING_PERF_SERIALIZER_H_

#include <stdint.h>
#include <sys/types.h>

#include <map>
#include <memory>
#include <vector>

#include "base/macros.h"

#include "compat/proto.h"
#include "compat/string.h"
#include "perf_data_utils.h"

struct perf_event_attr;

namespace quipper {

struct ParsedEvent;
struct PerfFileAttr;
struct PerfGroupDescMetadata;
struct PerfPMUMappingsMetadata;
struct PerfNodeTopologyMetadata;
struct PerfCPUTopologyMetadata;
struct PerfEventStats;
struct PerfParserOptions;
struct PerfUint32Metadata;
struct PerfUint64Metadata;

class SampleInfoReader;

class PerfSerializer {
 public:
  PerfSerializer();
  ~PerfSerializer();

  // The following functions convert between raw perf data structures and their
  // equivalent PerfDataProto representations.
  bool SerializePerfFileAttr(
      const PerfFileAttr& perf_file_attr,
      PerfDataProto_PerfFileAttr* perf_file_attr_proto) const;
  bool DeserializePerfFileAttr(
      const PerfDataProto_PerfFileAttr& perf_file_attr_proto,
      PerfFileAttr* perf_file_attr) const;

  bool SerializePerfEventAttr(
      const perf_event_attr& perf_event_attr,
      PerfDataProto_PerfEventAttr* perf_event_attr_proto) const;
  bool DeserializePerfEventAttr(
      const PerfDataProto_PerfEventAttr& perf_event_attr_proto,
      perf_event_attr* perf_event_attr) const;

  bool SerializePerfEventType(
      const PerfFileAttr& event_attr,
      PerfDataProto_PerfEventType* event_type_proto) const;
  bool DeserializePerfEventType(
      const PerfDataProto_PerfEventType& event_type_proto,
      PerfFileAttr* event_attr) const;

  bool SerializeEvent(const malloced_unique_ptr<event_t>& event_ptr,
                      PerfDataProto_PerfEvent* event_proto) const;
  bool DeserializeEvent(const PerfDataProto_PerfEvent& event_proto,
                        malloced_unique_ptr<event_t>* event_ptr) const;
  bool SerializeEventHeader(const perf_event_header& header,
                            PerfDataProto_EventHeader* header_proto) const;
  bool DeserializeEventHeader(const PerfDataProto_EventHeader& header_proto,
                              perf_event_header* header) const;

  bool SerializeSampleEvent(const event_t& event,
                            PerfDataProto_SampleEvent* sample) const;
  bool DeserializeSampleEvent(const PerfDataProto_SampleEvent& sample,
                              event_t* event) const;

  bool SerializeMMapEvent(const event_t& event,
                          PerfDataProto_MMapEvent* sample) const;
  bool DeserializeMMapEvent(const PerfDataProto_MMapEvent& sample,
                            event_t* event) const;

  bool SerializeMMap2Event(const event_t& event,
                           PerfDataProto_MMapEvent* sample) const;
  bool DeserializeMMap2Event(const PerfDataProto_MMapEvent& sample,
                             event_t* event) const;

  bool SerializeCommEvent(const event_t& event,
                          PerfDataProto_CommEvent* sample) const;
  bool DeserializeCommEvent(const PerfDataProto_CommEvent& sample,
                            event_t* event) const;

  // These handle both fork and exit events, which use the same protobuf
  // message definition.
  bool SerializeForkExitEvent(const event_t& event,
                              PerfDataProto_ForkEvent* sample) const;
  bool DeserializeForkExitEvent(const PerfDataProto_ForkEvent& sample,
                                event_t* event) const;

  bool SerializeLostEvent(const event_t& event,
                          PerfDataProto_LostEvent* sample) const;
  bool DeserializeLostEvent(const PerfDataProto_LostEvent& sample,
                            event_t* event) const;

  bool SerializeThrottleEvent(const event_t& event,
                              PerfDataProto_ThrottleEvent* sample) const;
  bool DeserializeThrottleEvent(const PerfDataProto_ThrottleEvent& sample,
                                event_t* event) const;

  bool SerializeReadEvent(const event_t& event,
                          PerfDataProto_ReadEvent* sample) const;
  bool DeserializeReadEvent(const PerfDataProto_ReadEvent& sample,
                            event_t* event) const;

  bool SerializeAuxEvent(const event_t& event,
                         PerfDataProto_AuxEvent* sample) const;
  bool DeserializeAuxEvent(const PerfDataProto_AuxEvent& sample,
                           event_t* event) const;

  bool SerializeSampleInfo(const event_t& event,
                           PerfDataProto_SampleInfo* sample_info) const;
  bool DeserializeSampleInfo(const PerfDataProto_SampleInfo& info,
                             event_t* event) const;

  bool SerializeTracingMetadata(const std::vector<char>& from,
                                PerfDataProto* to) const;
  bool DeserializeTracingMetadata(const PerfDataProto& from,
                                  std::vector<char>* to) const;

  bool SerializeBuildIDEvent(const malloced_unique_ptr<build_id_event>& from,
                             PerfDataProto_PerfBuildID* to) const;
  bool DeserializeBuildIDEvent(const PerfDataProto_PerfBuildID& from,
                               malloced_unique_ptr<build_id_event>* to) const;

  bool SerializeAuxtraceEvent(const event_t& event,
                              PerfDataProto_AuxtraceEvent* sample) const;
  bool SerializeAuxtraceEventTraceData(const std::vector<char>& from,
                                       PerfDataProto_AuxtraceEvent* to) const;
  bool DeserializeAuxtraceEvent(const PerfDataProto_AuxtraceEvent& sample,
                                event_t* event) const;
  bool DeserializeAuxtraceEventTraceData(
      const PerfDataProto_AuxtraceEvent& from, std::vector<char>* to) const;

  bool SerializeSingleUint32Metadata(
      const PerfUint32Metadata& metadata,
      PerfDataProto_PerfUint32Metadata* proto_metadata) const;
  bool DeserializeSingleUint32Metadata(
      const PerfDataProto_PerfUint32Metadata& proto_metadata,
      PerfUint32Metadata* metadata) const;

  bool SerializeSingleUint64Metadata(
      const PerfUint64Metadata& metadata,
      PerfDataProto_PerfUint64Metadata* proto_metadata) const;
  bool DeserializeSingleUint64Metadata(
      const PerfDataProto_PerfUint64Metadata& proto_metadata,
      PerfUint64Metadata* metadata) const;

  bool SerializeCPUTopologyMetadata(
      const PerfCPUTopologyMetadata& metadata,
      PerfDataProto_PerfCPUTopologyMetadata* proto_metadata) const;
  bool DeserializeCPUTopologyMetadata(
      const PerfDataProto_PerfCPUTopologyMetadata& proto_metadata,
      PerfCPUTopologyMetadata* metadata) const;

  bool SerializeNodeTopologyMetadata(
      const PerfNodeTopologyMetadata& metadata,
      PerfDataProto_PerfNodeTopologyMetadata* proto_metadata) const;
  bool DeserializeNodeTopologyMetadata(
      const PerfDataProto_PerfNodeTopologyMetadata& proto_metadata,
      PerfNodeTopologyMetadata* metadata) const;

  bool SerializePMUMappingsMetadata(
      const PerfPMUMappingsMetadata& metadata,
      PerfDataProto_PerfPMUMappingsMetadata* proto_metadata) const;
  bool DeserializePMUMappingsMetadata(
      const PerfDataProto_PerfPMUMappingsMetadata& proto_metadata,
      PerfPMUMappingsMetadata* metadata) const;

  bool SerializeGroupDescMetadata(
      const PerfGroupDescMetadata& metadata,
      PerfDataProto_PerfGroupDescMetadata* proto_metadata) const;
  bool DeserializeGroupDescMetadata(
      const PerfDataProto_PerfGroupDescMetadata& proto_metadata,
      PerfGroupDescMetadata* metadata) const;

  static void SerializeParserStats(const PerfEventStats& stats,
                                   PerfDataProto* perf_data_proto);
  static void DeserializeParserStats(const PerfDataProto& perf_data_proto,
                                     PerfEventStats* stats);

  // Instantiate a new PerfSampleReader with the given attr type. If an old one
  // exists for that attr type, it is discarded.
  void CreateSampleInfoReader(const PerfFileAttr& event_attr,
                              bool read_cross_endian);

  bool SampleInfoReaderAvailable() const {
    return !sample_info_reader_map_.empty();
  }

 private:
  // Special values for the event/other_event_id_pos_ fields.
  enum EventIdPosition {
    Uninitialized = -2,
    NotPresent = -1,
  };

  // Given a perf_event_attr, determines the offset of the ID field within an
  // event, relative to the start of sample info within an event. All attrs must
  // have the same ID field offset.
  void UpdateEventIdPositions(const struct perf_event_attr& attr);

  // Do non-SAMPLE events have a sample_id? Reflects the value of
  // sample_id_all in the first attr, which should be consistent accross all
  // attrs.
  bool SampleIdAll() const;

  // Find the event id in the event, and returns the corresponding
  // SampleInfoReader. Returns nullptr if a SampleInfoReader could not be found.
  const SampleInfoReader* GetSampleInfoReaderForEvent(
      const event_t& event) const;

  // Returns the SampleInfoReader associated with the given perf event ID, or
  // nullptr if none exists. |id| == 0 means there is no attr ID for each event
  // that associates it with a particular SampleInfoReader, in which case the
  // first available SampleInfoReader is returned.
  const SampleInfoReader* GetSampleInfoReaderForId(uint64_t id) const;

  // Reads the sample info fields from |event| into |sample_info|. If more than
  // one type of perf event attr is present, will pick the correct one. Also
  // returns a bitfield of available sample info fields for the attr, in
  // |sample_type|.
  // Returns true if successfully read.
  bool ReadPerfSampleInfoAndType(const event_t& event, perf_sample* sample_info,
                                 uint64_t* sample_type) const;

  bool SerializeKernelEvent(const event_t& event,
                            PerfDataProto_PerfEvent* event_proto) const;
  bool SerializeUserEvent(const event_t& event,
                          PerfDataProto_PerfEvent* event_proto) const;

  bool DeserializeKernelEvent(const PerfDataProto_PerfEvent& event_proto,
                              event_t* event) const;
  bool DeserializeUserEvent(const PerfDataProto_PerfEvent& event_proto,
                            event_t* event) const;

  // For SAMPLE events, the position of the sample id,
  // Or EventIdPosition::NotPresent if neither PERF_SAMPLE_ID(ENTIFIER) are set.
  // (Corresponds to evsel->id_pos in perf)
  ssize_t sample_event_id_pos_ = EventIdPosition::Uninitialized;
  // For non-SAMPLE events, the position of the sample id, counting backwards
  // from the end of the event.
  // Or EventIdPosition::NotPresent if neither PERF_SAMPLE_ID(ENTIFIER) are set.
  // (Corresponds to evsel->is_pos in perf)
  ssize_t other_event_id_pos_ = EventIdPosition::Uninitialized;

  // For each perf event attr ID, there is a SampleInfoReader to read events of
  // the associated perf attr type.
  std::map<uint64_t, std::unique_ptr<SampleInfoReader>> sample_info_reader_map_;

  DISALLOW_COPY_AND_ASSIGN(PerfSerializer);
};

}  // namespace quipper

#endif  // CHROMIUMOS_WIDE_PROFILING_PERF_SERIALIZER_H_