C++程序  |  453行  |  15.19 KB

/*
 * 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 SRC_TRACE_PROCESSOR_STORAGE_COLUMNS_H_
#define SRC_TRACE_PROCESSOR_STORAGE_COLUMNS_H_

#include <deque>
#include <limits>
#include <memory>
#include <string>
#include <vector>

#include "src/trace_processor/filtered_row_index.h"
#include "src/trace_processor/sqlite_utils.h"
#include "src/trace_processor/trace_storage.h"

namespace perfetto {
namespace trace_processor {

// A column of data backed by data storage.
class StorageColumn {
 public:
  struct Bounds {
    uint32_t min_idx = 0;
    uint32_t max_idx = std::numeric_limits<uint32_t>::max();
    bool consumed = false;
  };
  using Predicate = std::function<bool(uint32_t)>;
  using Comparator = std::function<int(uint32_t, uint32_t)>;

  StorageColumn(std::string col_name, bool hidden);
  virtual ~StorageColumn();

  // Implements StorageCursor::ColumnReporter.
  virtual void ReportResult(sqlite3_context*, uint32_t row) const = 0;

  // Given a SQLite operator and value for the comparision, returns a
  // predicate which takes in a row index and returns whether the row should
  // be returned.
  virtual void Filter(int op, sqlite3_value*, FilteredRowIndex*) const = 0;

  // Given a order by constraint for this column, returns a comparator
  // function which compares data in this column at two indices.
  virtual Comparator Sort(const QueryConstraints::OrderBy& ob) const = 0;

  // Returns the type of this column.
  virtual Table::ColumnType GetType() const = 0;

  // Bounds a filter on this column between a minimum and maximum index.
  // Generally this is only possible if the column is sorted.
  virtual Bounds BoundFilter(int, sqlite3_value*) const { return Bounds{}; }

  // Returns whether this column is ordered.
  virtual bool HasOrdering() const { return false; }

  const std::string& name() const { return col_name_; }
  bool hidden() const { return hidden_; }

 private:
  std::string col_name_;
  bool hidden_ = false;
};

// The implementation of StorageColumn for Strings.
// The actual retrieval of the numerics from the data types is left to the
// Acessor trait (see below for definition).
template <typename Accessor /* <NullTermStringView> */>
class StringColumn final : public StorageColumn {
 public:
  StringColumn(std::string col_name, Accessor accessor, bool hidden = false)
      : StorageColumn(col_name, hidden), accessor_(accessor) {}

  void ReportResult(sqlite3_context* ctx, uint32_t row) const override {
    NullTermStringView str = accessor_.Get(row);
    if (str.c_str() == nullptr) {
      sqlite3_result_null(ctx);
    } else {
      sqlite3_result_text(ctx, str.c_str(), -1, sqlite_utils::kSqliteStatic);
    }
  }

  Bounds BoundFilter(int, sqlite3_value*) const override {
    Bounds bounds;
    bounds.max_idx = static_cast<uint32_t>(accessor_.Size());
    return bounds;
  }

  void Filter(int, sqlite3_value*, FilteredRowIndex*) const override {}

  Comparator Sort(const QueryConstraints::OrderBy& ob) const override {
    if (ob.desc) {
      return [this](uint32_t f, uint32_t s) {
        NullTermStringView a = accessor_.Get(f);
        NullTermStringView b = accessor_.Get(s);
        return sqlite_utils::CompareValuesDesc(a, b);
      };
    }
    return [this](uint32_t f, uint32_t s) {
      NullTermStringView a = accessor_.Get(f);
      NullTermStringView b = accessor_.Get(s);
      return sqlite_utils::CompareValuesAsc(a, b);
    };
  }

  Table::ColumnType GetType() const override {
    return Table::ColumnType::kString;
  }

  bool HasOrdering() const override { return accessor_.HasOrdering(); }

 private:
  Accessor accessor_;
};

// The implementation of StorageColumn for numeric data types.
// The actual retrieval of the numerics from the data types is left to the
// Acessor trait (see below for definition).
template <typename Accessor,
          typename sqlite_utils::is_numeric<typename Accessor::Type>* = nullptr>
class NumericColumn : public StorageColumn {
 public:
  // The type of the column. This is one of uint32_t, int32_t, uint64_t etc.
  using NumericType = typename Accessor::Type;

  NumericColumn(std::string col_name, bool hidden, Accessor accessor)
      : StorageColumn(col_name, hidden), accessor_(accessor) {}
  ~NumericColumn() override = default;

  void ReportResult(sqlite3_context* ctx, uint32_t row) const override {
    sqlite_utils::ReportSqliteResult(ctx, accessor_.Get(row));
  }

  Bounds BoundFilter(int op, sqlite3_value* sqlite_val) const override {
    Bounds bounds;
    bounds.max_idx = accessor_.Size();

    if (!accessor_.HasOrdering())
      return bounds;

    // Makes the below code much more readable.
    using namespace sqlite_utils;

    NumericType min = kTMin;
    NumericType max = kTMax;
    if (IsOpGe(op) || IsOpGt(op)) {
      min = FindGtBound<NumericType>(IsOpGe(op), sqlite_val);
    } else if (IsOpLe(op) || IsOpLt(op)) {
      max = FindLtBound<NumericType>(IsOpLe(op), sqlite_val);
    } else if (IsOpEq(op)) {
      auto val = FindEqBound<NumericType>(sqlite_val);
      min = val;
      max = val;
    }

    if (min <= kTMin && max >= kTMax)
      return bounds;

    bounds.min_idx = accessor_.LowerBoundIndex(min);
    bounds.max_idx = accessor_.UpperBoundIndex(max);
    bounds.consumed = true;

    return bounds;
  }

  void Filter(int op,
              sqlite3_value* value,
              FilteredRowIndex* index) const override {
    auto type = sqlite3_value_type(value);

    bool same_type = (kIsIntegralType && type == SQLITE_INTEGER) ||
                     (kIsRealType && type == SQLITE_FLOAT);
    if (sqlite_utils::IsOpEq(op) && same_type &&
        accessor_.CanFindEqualIndices()) {
      auto raw = sqlite_utils::ExtractSqliteValue<NumericType>(value);
      index->IntersectRows(accessor_.EqualIndices(raw));
      return;
    }

    if (kIsIntegralType && (type == SQLITE_INTEGER || type == SQLITE_NULL)) {
      FilterWithCast<int64_t>(op, value, index);
    } else if (type == SQLITE_INTEGER || type == SQLITE_FLOAT ||
               type == SQLITE_NULL) {
      FilterWithCast<double>(op, value, index);
    } else {
      PERFETTO_FATAL("Unexpected sqlite value to compare against");
    }
  }

  Comparator Sort(const QueryConstraints::OrderBy& ob) const override {
    if (ob.desc) {
      return [this](uint32_t f, uint32_t s) {
        return sqlite_utils::CompareValuesDesc(accessor_.Get(f),
                                               accessor_.Get(s));
      };
    }
    return [this](uint32_t f, uint32_t s) {
      return sqlite_utils::CompareValuesAsc(accessor_.Get(f), accessor_.Get(s));
    };
  }

  bool HasOrdering() const override { return accessor_.HasOrdering(); }

  Table::ColumnType GetType() const override {
    if (std::is_same<NumericType, int32_t>::value) {
      return Table::ColumnType::kInt;
    } else if (std::is_same<NumericType, uint8_t>::value ||
               std::is_same<NumericType, uint32_t>::value) {
      return Table::ColumnType::kUint;
    } else if (std::is_same<NumericType, int64_t>::value) {
      return Table::ColumnType::kLong;
    } else if (std::is_same<NumericType, double>::value) {
      return Table::ColumnType::kDouble;
    }
    PERFETTO_FATAL("Unexpected column type");
  }

 private:
  static constexpr bool kIsIntegralType = std::is_integral<NumericType>::value;
  static constexpr bool kIsRealType =
      std::is_floating_point<NumericType>::value;

  NumericType kTMin = std::numeric_limits<NumericType>::lowest();
  NumericType kTMax = std::numeric_limits<NumericType>::max();

  // Filters the rows of this column by creating the predicate from the sqlite
  // value using type |UpcastNumericType| and casting data from the column
  // to also be this type.
  // Note: We cast here to make numeric comparisions as accurate as possible.
  // For example, suppose NumericType == uint32_t and the sqlite value has
  // an integer. Then UpcastNumericType == int64_t because uint32_t can be
  // upcast to an int64_t and it's the most generic type we can compare using.
  // Alternatively if either the column or sqlite value is real, we will always
  // cast to a double before comparing.
  template <typename UpcastNumericType>
  void FilterWithCast(int op,
                      sqlite3_value* value,
                      FilteredRowIndex* index) const {
    auto predicate =
        sqlite_utils::CreateNumericPredicate<UpcastNumericType>(op, value);
    auto cast_predicate = [this,
                           predicate](uint32_t row) PERFETTO_ALWAYS_INLINE {
      return predicate(static_cast<UpcastNumericType>(accessor_.Get(row)));
    };
    index->FilterRows(cast_predicate);
  }

  Accessor accessor_;
};

// Defines an accessor for columns.
// An accessor is a abstraction over the method to retrieve data in a column. As
// there are many possible types of backing data (std::vector, std::deque,
// creating on the flight etc.), this class hides this complexity behind an
// interface to let the column implementation focus on actually interfacing
// with SQLite and rest of trace processor.
// This class exists as an interface for documentation purposes. There should
// be no use of it apart from classes inheriting from it to ensure they comply
// with the requirements of the interface.
template <typename DataType>
class Accessor {
 public:
  using Type = DataType;

  virtual ~Accessor() = default;

  // Returns the number of elements in the backing storage.
  virtual uint32_t Size() const = 0;

  // Returns the element located at index |idx|.
  virtual Type Get(uint32_t idx) const = 0;

  // Returns whether the backing data source is ordered. |LowerBoundIndex| and
  // |UpperBoundIndex| will be called only if HasOrdering() returns true.
  virtual bool HasOrdering() const { return false; }

  // Returns the index of the lower bound of the value.
  virtual uint32_t LowerBoundIndex(Type) const { PERFETTO_CHECK(false); }

  // Returns the index of the lower bound of the value.
  virtual uint32_t UpperBoundIndex(Type) const { PERFETTO_CHECK(false); }

  // Returns whether the backing data sources can efficiently provide the
  // indices of elements equal to a given value. |EqualIndices| will be called
  // only if |CanFindEqualIndices| returns true.
  virtual bool CanFindEqualIndices() const { return false; }

  // Returns the indices into the backing data source with value equal to
  // |value|.
  virtual std::vector<uint32_t> EqualIndices(Type) const {
    PERFETTO_CHECK(false);
  }
};

// An accessor implementation for string which uses a deque to store offsets
// into a StringPool.
class StringPoolAccessor : public Accessor<NullTermStringView> {
 public:
  StringPoolAccessor(const std::deque<StringPool::Id>* deque,
                     const StringPool* string_pool);
  ~StringPoolAccessor() override;

  uint32_t Size() const override {
    return static_cast<uint32_t>(deque_->size());
  }

  NullTermStringView Get(uint32_t idx) const override {
    return string_pool_->Get((*deque_)[idx]);
  }

 private:
  const std::deque<StringPool::Id>* deque_;
  const StringPool* string_pool_;
};

// An accessor implementation for string which uses a deque to store indices
// into a vector of strings.
template <typename Id>
class StringVectorAccessor : public Accessor<NullTermStringView> {
 public:
  StringVectorAccessor(const std::deque<Id>* deque,
                       const std::vector<const char*>* string_map)
      : deque_(deque), string_map_(string_map) {}
  ~StringVectorAccessor() override = default;

  uint32_t Size() const override {
    return static_cast<uint32_t>(deque_->size());
  }

  NullTermStringView Get(uint32_t idx) const override {
    const char* ptr = (*string_map_)[(*deque_)[idx]];
    return ptr ? NullTermStringView(ptr) : NullTermStringView();
  }

 private:
  const std::deque<Id>* deque_;
  const std::vector<const char*>* string_map_;
};

// An accessor implementation for numeric columns which uses a deque as the
// backing storage with an opitonal index for quick equality filtering.
template <typename NumericType>
class NumericDequeAccessor : public Accessor<NumericType> {
 public:
  NumericDequeAccessor(const std::deque<NumericType>* deque,
                       const std::deque<std::vector<uint32_t>>* index,
                       bool has_ordering)
      : deque_(deque), index_(index), has_ordering_(has_ordering) {}
  ~NumericDequeAccessor() override = default;

  uint32_t Size() const override {
    return static_cast<uint32_t>(deque_->size());
  }

  NumericType Get(uint32_t idx) const override { return (*deque_)[idx]; }

  bool HasOrdering() const override { return has_ordering_; }

  uint32_t LowerBoundIndex(NumericType value) const override {
    PERFETTO_DCHECK(HasOrdering());
    auto it = std::lower_bound(deque_->begin(), deque_->end(), value);
    return static_cast<uint32_t>(std::distance(deque_->begin(), it));
  }

  uint32_t UpperBoundIndex(NumericType value) const override {
    PERFETTO_DCHECK(HasOrdering());
    auto it = std::upper_bound(deque_->begin(), deque_->end(), value);
    return static_cast<uint32_t>(std::distance(deque_->begin(), it));
  }

  bool CanFindEqualIndices() const override {
    return std::is_integral<NumericType>::value && index_ != nullptr;
  }

  std::vector<uint32_t> EqualIndices(NumericType value) const override {
    PERFETTO_DCHECK(CanFindEqualIndices());
    if (value < 0 || static_cast<size_t>(value) >= index_->size())
      return {};
    return (*index_)[static_cast<size_t>(value)];
  }

 private:
  const std::deque<NumericType>* deque_ = nullptr;
  const std::deque<std::vector<uint32_t>>* index_ = nullptr;
  bool has_ordering_ = false;
};

class TsEndAccessor : public Accessor<int64_t> {
 public:
  TsEndAccessor(const std::deque<int64_t>* ts, const std::deque<int64_t>* dur);
  ~TsEndAccessor() override;

  uint32_t Size() const override { return static_cast<uint32_t>(ts_->size()); }

  int64_t Get(uint32_t idx) const override {
    return (*ts_)[idx] + (*dur_)[idx];
  }

 private:
  const std::deque<int64_t>* ts_ = nullptr;
  const std::deque<int64_t>* dur_ = nullptr;
};

class RowIdAccessor : public Accessor<int64_t> {
 public:
  RowIdAccessor(TableId table_id);
  ~RowIdAccessor() override;

  uint32_t Size() const override {
    return std::numeric_limits<uint32_t>::max();
  }

  int64_t Get(uint32_t idx) const override {
    return TraceStorage::CreateRowId(table_id_, idx);
  }

 private:
  TableId table_id_;
};

class RowAccessor : public Accessor<uint32_t> {
 public:
  RowAccessor();
  ~RowAccessor() override;

  uint32_t Size() const override {
    return std::numeric_limits<uint32_t>::max();
  }

  uint32_t Get(uint32_t idx) const override { return idx; }

  bool HasOrdering() const override { return true; }

  uint32_t LowerBoundIndex(uint32_t idx) const override { return idx; }

  uint32_t UpperBoundIndex(uint32_t idx) const override { return idx + 1; }
};

}  // namespace trace_processor
}  // namespace perfetto

#endif  // SRC_TRACE_PROCESSOR_STORAGE_COLUMNS_H_