<commit_before>/*-------------------------------------------------------------------------
 *
 * dll.cpp
 * file description
 *
 * Copyright(c) 2015, CMU
 *
 * /n-store/src/bridge/ddl.cpp
 *
 *-------------------------------------------------------------------------
 */

#include "postgres.h"
#include "c.h"

#include "bridge/bridge.h"
#include "nodes/parsenodes.h"
#include "parser/parse_utilcmd.h"
#include "parser/parse_type.h"
#include "access/htup_details.h"
#include "utils/resowner.h"
#include "utils/syscache.h"
#include "catalog/pg_type.h"

#include "backend/bridge/ddl.h"
#include "backend/catalog/catalog.h"
#include "backend/catalog/schema.h"
#include "backend/common/logger.h"
#include "backend/common/types.h"
#include "backend/index/index.h"
#include "backend/index/index_factory.h"
#include "backend/storage/backend_vm.h"
#include "backend/storage/table_factory.h"

#include <cassert>
#include <iostream>

namespace peloton {
namespace bridge {

static std::vector<IndexInfo> index_infos;

/**
 * @brief Process utility statement.
 * @param parsetree Parse tree
 */
void DDL::ProcessUtility(Node *parsetree,
                         const char *queryString){
  assert(parsetree != nullptr);
  assert(queryString != nullptr);

  // Process depending on type of utility statement
  switch (nodeTag(parsetree))
  {
    case T_CreateStmt:
    case T_CreateForeignTableStmt:
    {
      List     *stmts;
      ListCell   *l;

      assert(CurrentResourceOwner != NULL);

      /* Run parse analysis ... */
      stmts = transformCreateStmt((CreateStmt *) parsetree,
                                  queryString);

      /* ... and do it */
      foreach(l, stmts)
      {
        Node     *stmt = (Node *) lfirst(l);
        if (IsA(stmt, CreateStmt))
        {
          CreateStmt* Cstmt = (CreateStmt*)stmt;
          List* schema = (List*)(Cstmt->tableElts);

          char* relation_name = Cstmt->relation->relname;
          std::vector<peloton::catalog::ColumnInfo> column_infos;
          std::vector<std::string> reference_table_names;

          bool status;

          // Construct DDL_ColumnInfo with schema
          if( schema != NULL ){
            column_infos = peloton::bridge::DDL::ConstructColumnInfoByParsingCreateStmt( Cstmt, reference_table_names );
            status = peloton::bridge::DDL::CreateTable( relation_name, column_infos );
          }
          else {
            // Create Table without column info
            status = peloton::bridge::DDL::CreateTable( relation_name, column_infos );
          }

          fprintf(stderr, "DDL_CreateTable(%s) :: %d \n", relation_name, status);

          // TODO :: Change location or
          // Foreign keys
          for( auto reference_table_name : reference_table_names ){
               oid_t database_oid = GetCurrentDatabaseOid();
               assert( database_oid );
               storage::DataTable* current_table = (storage::DataTable*) catalog::Manager::GetInstance().GetLocation(database_oid, relation_name);
               storage::DataTable* reference_table = (storage::DataTable*) catalog::Manager::GetInstance().GetLocation(database_oid, reference_table_name);

               current_table->AddReferenceTable(reference_table);
          }

        }
      }
    }

    break;

    case T_IndexStmt:  /* CREATE INDEX */
    {
      bool status;
      IndexStmt  *Istmt = (IndexStmt *) parsetree;

      // Construct IndexInfo
      IndexInfo* index_info = ConstructIndexInfoByParsingIndexStmt( Istmt );

      // If this index is either unique or primary key, store the index information and skip
      // the rest part since the table has not been created yet.
      if( Istmt->isconstraint ){
         index_infos.push_back(*index_info);
         break;
      }

      status = peloton::bridge::DDL::CreateIndex( index_info->GetIndexName(),
                                                  index_info->GetTableName(),
                                                  index_info->GetMethodType(),
                                                  index_info->GetType(),
                                                  index_info->IsUnique(),
                                                  index_info->GetKeyColumnNames());

      fprintf(stderr, "DDLCreateIndex :: %d \n", status);

    }
    break;

    case T_DropStmt:
    { DropStmt* drop;
      ListCell  *cell;
      int table_oid_itr = 0;
      bool ret;
      Oid* table_oid_list;
      drop = (DropStmt*) parsetree;
      table_oid_list = (Oid*) malloc ( sizeof(Oid)*(drop->objects->length));

      foreach(cell, drop->objects)
      {
        if (drop->removeType == OBJECT_TABLE )
        {
          List* names = ((List *) lfirst(cell));
          char* table_name = strVal(linitial(names));
          table_oid_list[table_oid_itr++] = GetRelationOid(table_name);
        }
      }

      while(table_oid_itr > 0)
      {
        ret  = peloton::bridge::DDL::DropTable(table_oid_list[--table_oid_itr]);
        fprintf(stderr, "DDLDropTable :: %d \n", ret);
      }

    }
    break;

    default:
      elog(LOG, "unrecognized node type: %d",
           (int) nodeTag(parsetree));
      break;
  }

}

/**
 * @brief Construct ColumnInfo vector from a create statement
 * @param Cstmt a create statement
 * @return ColumnInfo vector
 */
std::vector<catalog::ColumnInfo> DDL::ConstructColumnInfoByParsingCreateStmt( CreateStmt* Cstmt, std::vector<std::string>& reference_table_names){
  assert(Cstmt);

  // Get the column list from the create statement
  List* ColumnList = (List*)(Cstmt->tableElts);
  std::vector<catalog::ColumnInfo> column_infos;

  //===--------------------------------------------------------------------===//
  // Column Type Information
  //===--------------------------------------------------------------------===//

  // Parse the CreateStmt and construct ColumnInfo
  ListCell   *entry;
  foreach(entry, ColumnList){

    ColumnDef  *coldef = lfirst(entry);

    // Parsing the column value type
    Oid typeoid;
    int32 typemod;
    int typelen;

    // Get the type oid and type mod with given typeName
    typeoid = typenameTypeId(NULL, coldef->typeName);
    typenameTypeIdAndMod(NULL, coldef->typeName, &typeoid, &typemod);

    // Get type length
    Type tup = typeidType(typeoid);
    typelen = typeLen(tup);
    ReleaseSysCache(tup);

    /* TODO :: Simple version, but need to check whether it is the same with above or not
    // Get the type oid
    typeoid = typenameTypeId(NULL, coldef->typeName);

    // type mod
    typemod = get_typmodin(typeoid);

    // Get type length
    typelen = get_typlen(typeoid);
    */

    // For a fixed-size type, typlen is the number of bytes in the internal
    // representation of the type. But for a variable-length type, typlen is negative.
    if( typelen == - 1 )
      typelen = typemod;

    ValueType column_valueType = PostgresValueTypeToPelotonValueType( (PostgresValueType) typeoid );
    int column_length = typelen;
    std::string column_name = coldef->colname;

    //===--------------------------------------------------------------------===//
    // Column Constraint Information
    //===--------------------------------------------------------------------===//
    std::vector<catalog::Constraint> column_constraints;

    if( coldef->constraints != NULL){
      ListCell* constNodeEntry;

      foreach(constNodeEntry, coldef->constraints)
      {
        Constraint* ConstraintNode = lfirst(constNodeEntry);
        ConstraintType contype;
        std::string conname;
        std::string reference_table_name;

        // Get constraint type
        contype = PostgresConstraintTypeToPelotonConstraintType( (PostgresConstraintType) ConstraintNode->contype );

        if( ConstraintNode->pktable != NULL ){
          reference_table_name = ConstraintNode->pktable->relname;
          reference_table_names.push_back( reference_table_name );
        }

        // Get constraint name
        if( ConstraintNode->conname != NULL)
          conname = ConstraintNode->conname;

        catalog::Constraint* constraint = new catalog::Constraint( contype, conname, reference_table_name );
        column_constraints.push_back(*constraint);
      }
    }// end of parsing constraint

    catalog::ColumnInfo *column_info = new catalog::ColumnInfo( column_valueType,
                                                                column_length,
                                                                column_name,
                                                                column_constraints);

    // Insert column_info into ColumnInfos
    column_infos.push_back(*column_info);
  }// end of parsing column list

  return column_infos;
}

/**
 * @brief Construct IndexInfo from a index statement
 * @param Istmt an index statement
 * @return IndexInfo
 */
IndexInfo* DDL::ConstructIndexInfoByParsingIndexStmt( IndexStmt* Istmt ){
  std::string index_name;
  std::string table_name;
  IndexMethodType method_type;
  IndexType type;
  bool unique_keys;
  std::vector<std::string> key_column_names;

  // Table name
  table_name = Istmt->relation->relname;

  // Index name and index type
  if( Istmt->idxname == NULL && Istmt->isconstraint ){
    if( Istmt->primary ) {
      index_name = table_name+"_pkey";
      type = INDEX_TYPE_PRIMARY_KEY;
    }else if( Istmt->unique ){
      index_name = table_name+"_key";
      type = INDEX_TYPE_UNIQUE;
    }
  }else{
    type = INDEX_TYPE_NORMAL;
  }

  // Key column names
  ListCell   *entry;
  foreach(entry, Istmt->indexParams){
    IndexElem *indexElem = lfirst(entry);
    if( indexElem->name != NULL ){
      key_column_names.push_back(indexElem->name);
    }
  }

  // Index method type
  // TODO :: More access method types need
  method_type = INDEX_METHOD_TYPE_BTREE_MULTIMAP;

  IndexInfo* index_info = new IndexInfo( index_name,
                                         table_name,
                                         method_type,
                                         type,
                                         Istmt->unique,
                                         key_column_names);
  return index_info;
}

/**
 * @brief Create table.
 * @param table_name Table name
 * @param column_infos Information about the columns
 * @param schema Schema for the table
 * @return true if we created a table, false otherwise
 */
bool DDL::CreateTable( std::string table_name,
                        std::vector<catalog::ColumnInfo> column_infos,
                        catalog::Schema *schema){

  //===--------------------------------------------------------------------===//
  // Check Parameters
  //===--------------------------------------------------------------------===//
  assert( !table_name.empty() );
  assert( column_infos.size() > 0 || schema != NULL );

  Oid database_oid = GetCurrentDatabaseOid();
  if(database_oid == InvalidOid)
    return false;

  // Construct our schema from vector of ColumnInfo
  if( schema == NULL)
    schema = new catalog::Schema(column_infos);

  // FIXME: Construct table backend
  storage::VMBackend *backend = new storage::VMBackend();

  // Build a table from schema
  storage::DataTable *table = storage::TableFactory::GetDataTable(database_oid, schema, table_name);
  catalog::Schema* our_schema = table->GetSchema();

  /* DEBUGGING */
  //std::cout << "table name : " << table_name << "\n" << *our_schema << std::endl;

  // In Postgres, indexes such as primary key index, unique indexy are created before table.
  // In Peloton, however, the table is required to create an index. For that reason, index information
  // was stored before and now it will be created.
  for( auto index_info : index_infos){
    bool status;
    status = peloton::bridge::DDL::CreateIndex( index_info.GetIndexName(),
        index_info.GetTableName(),
        index_info.GetMethodType(),
        index_info.GetType(),
        index_info.IsUnique(),
        index_info.GetKeyColumnNames());
    fprintf(stderr, "DDLCreateIndex :: %d \n", status);
  }
  index_infos.clear();

 if(table != nullptr) {
    LOG_INFO("Created table : %s", table_name.c_str());
    return true;
  }

  return false;
}

/**
 * @brief Drop table.
 * @param table_oid Table id.
 * @return true if we dropped the table, false otherwise
 */
bool DDL::DropTable(Oid table_oid) {

  oid_t database_oid = GetCurrentDatabaseOid();

  if(database_oid == InvalidOid || table_oid == InvalidOid) {
    LOG_WARN("Could not drop table :: db oid : %u table oid : %u", database_oid, table_oid);
    return false;
  }

  bool status = storage::TableFactory::DropDataTable(database_oid, table_oid);
  if(status == true) {
    LOG_INFO("Dropped table with oid : %u\n", table_oid);
    return true;
  }

  return false;
}

/**
 * @brief Create index.
 * @param index_name Index name
 * @param table_name Table name
 * @param type Type of the index
 * @param unique Index is unique or not ?
 * @param key_column_names column names for the key table
 * @return true if we create the index, false otherwise
 */
bool DDL::CreateIndex(std::string index_name,
                      std::string table_name,
                      IndexMethodType  index_method_type,
                      IndexType  index_type,
                      bool unique_keys,
                      std::vector<std::string> key_column_names,
                      bool bootstrap ){

  assert( !index_name.empty() );
  assert( !table_name.empty() );
  assert( key_column_names.size() > 0  );

  // NOTE: We currently only support btree as our index implementation
  // TODO : Support other types based on "type" argument
  IndexMethodType our_index_type = INDEX_METHOD_TYPE_BTREE_MULTIMAP;

  // Get the database oid and table oid
  oid_t database_oid = GetCurrentDatabaseOid();
  assert( database_oid );

  //oid_t table_oid = GetRelationOid(table_name.c_str());

  // Get the table location from manager
  auto table = catalog::Manager::GetInstance().GetLocation(database_oid, table_name);
  storage::DataTable* data_table = (storage::DataTable*) table;
  auto tuple_schema = data_table->GetSchema();

  // Construct key schema
  std::vector<oid_t> key_columns;

  // Based on the key column info, get the oid of the given 'key' columns in the tuple schema
  for( auto key_column_name : key_column_names ){
    for( oid_t tuple_schema_column_itr = 0; tuple_schema_column_itr < tuple_schema->GetColumnCount();
        tuple_schema_column_itr++){

      // Get the current column info from tuple schema
      catalog::ColumnInfo column_info = tuple_schema->GetColumnInfo(tuple_schema_column_itr);
      // Compare Key Schema's current column name and Tuple Schema's current column name
      if( key_column_name == column_info.name ){
        key_columns.push_back(tuple_schema_column_itr);

        // TODO :: Need to talk with Joy
        // NOTE :: Since pg_attribute doesn't have any information about primary key and unique key,
        //         I try to store these information when we create an unique and primary key index
        if( bootstrap ){
          if( index_type == INDEX_TYPE_PRIMARY_KEY ){
            catalog::Constraint* constraint = new catalog::Constraint( CONSTRAINT_TYPE_PRIMARY );
            tuple_schema->AddConstraintInColumn( tuple_schema_column_itr, constraint);
          }else if( index_type == INDEX_TYPE_UNIQUE ){
            catalog::Constraint* constraint = new catalog::Constraint( CONSTRAINT_TYPE_UNIQUE );
            tuple_schema->AddConstraintInColumn( tuple_schema_column_itr, constraint);
          }
        }

      }
    }
  }

  auto key_schema = catalog::Schema::CopySchema(tuple_schema, key_columns);

  // Create index metadata and physical index
  index::IndexMetadata* metadata = new index::IndexMetadata(index_name, our_index_type,
                                                            tuple_schema, key_schema,
                                                            unique_keys);
  index::Index* index = index::IndexFactory::GetInstance(metadata);

  // Record the built index in the table
  switch( index_type ){
  case INDEX_TYPE_NORMAL:
	  data_table->AddIndex(index);
	  break;
  case INDEX_TYPE_PRIMARY_KEY:
	  data_table->SetPrimaryIndex(index);
	  break;
  case INDEX_TYPE_UNIQUE:
	  data_table->AddUniqueIndex(index);
	  break;
  default:
      elog(LOG, "unrecognized index type: %d", index_type);
  }

  return true;
}

} // namespace bridge
} // namespace peloton
<commit_msg>Minor fix - unique index name<commit_after>/*-------------------------------------------------------------------------
 *
 * dll.cpp
 * file description
 *
 * Copyright(c) 2015, CMU
 *
 * /n-store/src/bridge/ddl.cpp
 *
 *-------------------------------------------------------------------------
 */

#include "postgres.h"
#include "c.h"

#include "bridge/bridge.h"
#include "nodes/parsenodes.h"
#include "parser/parse_utilcmd.h"
#include "parser/parse_type.h"
#include "access/htup_details.h"
#include "utils/resowner.h"
#include "utils/syscache.h"
#include "catalog/pg_type.h"

#include "backend/bridge/ddl.h"
#include "backend/catalog/catalog.h"
#include "backend/catalog/schema.h"
#include "backend/common/logger.h"
#include "backend/common/types.h"
#include "backend/index/index.h"
#include "backend/index/index_factory.h"
#include "backend/storage/backend_vm.h"
#include "backend/storage/table_factory.h"

#include <cassert>
#include <iostream>

namespace peloton {
namespace bridge {

static std::vector<IndexInfo> index_infos;

/**
 * @brief Process utility statement.
 * @param parsetree Parse tree
 */
void DDL::ProcessUtility(Node *parsetree,
                         const char *queryString){
  assert(parsetree != nullptr);
  assert(queryString != nullptr);

  // Process depending on type of utility statement
  switch (nodeTag(parsetree))
  {
    case T_CreateStmt:
    case T_CreateForeignTableStmt:
    {
      List     *stmts;
      ListCell   *l;

      assert(CurrentResourceOwner != NULL);

      /* Run parse analysis ... */
      stmts = transformCreateStmt((CreateStmt *) parsetree,
                                  queryString);

      /* ... and do it */
      foreach(l, stmts)
      {
        Node     *stmt = (Node *) lfirst(l);
        if (IsA(stmt, CreateStmt))
        {
          CreateStmt* Cstmt = (CreateStmt*)stmt;
          List* schema = (List*)(Cstmt->tableElts);

          char* relation_name = Cstmt->relation->relname;
          std::vector<peloton::catalog::ColumnInfo> column_infos;
          std::vector<std::string> reference_table_names;

          bool status;

          // Construct DDL_ColumnInfo with schema
          if( schema != NULL ){
            column_infos = peloton::bridge::DDL::ConstructColumnInfoByParsingCreateStmt( Cstmt, reference_table_names );
            status = peloton::bridge::DDL::CreateTable( relation_name, column_infos );
          }
          else {
            // Create Table without column info
            status = peloton::bridge::DDL::CreateTable( relation_name, column_infos );
          }

          fprintf(stderr, "DDL_CreateTable(%s) :: %d \n", relation_name, status);

          // TODO :: Change location or
          // Foreign keys
          for( auto reference_table_name : reference_table_names ){
               oid_t database_oid = GetCurrentDatabaseOid();
               assert( database_oid );
               storage::DataTable* current_table = (storage::DataTable*) catalog::Manager::GetInstance().GetLocation(database_oid, relation_name);
               storage::DataTable* reference_table = (storage::DataTable*) catalog::Manager::GetInstance().GetLocation(database_oid, reference_table_name);

               current_table->AddReferenceTable(reference_table);
          }

        }
      }
    }

    break;

    case T_IndexStmt:  /* CREATE INDEX */
    {
      bool status;
      IndexStmt  *Istmt = (IndexStmt *) parsetree;

      // Construct IndexInfo
      IndexInfo* index_info = ConstructIndexInfoByParsingIndexStmt( Istmt );

      // If this index is either unique or primary key, store the index information and skip
      // the rest part since the table has not been created yet.
      if( Istmt->isconstraint ){
         index_infos.push_back(*index_info);
         break;
      }

      status = peloton::bridge::DDL::CreateIndex( index_info->GetIndexName(),
                                                  index_info->GetTableName(),
                                                  index_info->GetMethodType(),
                                                  index_info->GetType(),
                                                  index_info->IsUnique(),
                                                  index_info->GetKeyColumnNames());

      fprintf(stderr, "DDLCreateIndex %s :: %d \n", index_info->GetIndexName().c_str(), status);

    }
    break;

    case T_DropStmt:
    { DropStmt* drop;
      ListCell  *cell;
      int table_oid_itr = 0;
      bool ret;
      Oid* table_oid_list;
      drop = (DropStmt*) parsetree;
      table_oid_list = (Oid*) malloc ( sizeof(Oid)*(drop->objects->length));

      foreach(cell, drop->objects)
      {
        if (drop->removeType == OBJECT_TABLE )
        {
          List* names = ((List *) lfirst(cell));
          char* table_name = strVal(linitial(names));
          table_oid_list[table_oid_itr++] = GetRelationOid(table_name);
        }
      }

      while(table_oid_itr > 0)
      {
        ret  = peloton::bridge::DDL::DropTable(table_oid_list[--table_oid_itr]);
        fprintf(stderr, "DDLDropTable :: %d \n", ret);
      }

    }
    break;

    default:
      elog(LOG, "unrecognized node type: %d",
           (int) nodeTag(parsetree));
      break;
  }

}

/**
 * @brief Construct ColumnInfo vector from a create statement
 * @param Cstmt a create statement
 * @return ColumnInfo vector
 */
std::vector<catalog::ColumnInfo> DDL::ConstructColumnInfoByParsingCreateStmt( CreateStmt* Cstmt, std::vector<std::string>& reference_table_names){
  assert(Cstmt);

  // Get the column list from the create statement
  List* ColumnList = (List*)(Cstmt->tableElts);
  std::vector<catalog::ColumnInfo> column_infos;

  //===--------------------------------------------------------------------===//
  // Column Type Information
  //===--------------------------------------------------------------------===//

  // Parse the CreateStmt and construct ColumnInfo
  ListCell   *entry;
  foreach(entry, ColumnList){

    ColumnDef  *coldef = lfirst(entry);

    // Parsing the column value type
    Oid typeoid;
    int32 typemod;
    int typelen;

    // Get the type oid and type mod with given typeName
    typeoid = typenameTypeId(NULL, coldef->typeName);
    typenameTypeIdAndMod(NULL, coldef->typeName, &typeoid, &typemod);

    // Get type length
    Type tup = typeidType(typeoid);
    typelen = typeLen(tup);
    ReleaseSysCache(tup);

    /* TODO :: Simple version, but need to check whether it is the same with above or not
    // Get the type oid
    typeoid = typenameTypeId(NULL, coldef->typeName);

    // type mod
    typemod = get_typmodin(typeoid);

    // Get type length
    typelen = get_typlen(typeoid);
    */

    // For a fixed-size type, typlen is the number of bytes in the internal
    // representation of the type. But for a variable-length type, typlen is negative.
    if( typelen == - 1 )
      typelen = typemod;

    ValueType column_valueType = PostgresValueTypeToPelotonValueType( (PostgresValueType) typeoid );
    int column_length = typelen;
    std::string column_name = coldef->colname;

    //===--------------------------------------------------------------------===//
    // Column Constraint Information
    //===--------------------------------------------------------------------===//
    std::vector<catalog::Constraint> column_constraints;

    if( coldef->constraints != NULL){
      ListCell* constNodeEntry;

      foreach(constNodeEntry, coldef->constraints)
      {
        Constraint* ConstraintNode = lfirst(constNodeEntry);
        ConstraintType contype;
        std::string conname;
        std::string reference_table_name;

        // Get constraint type
        contype = PostgresConstraintTypeToPelotonConstraintType( (PostgresConstraintType) ConstraintNode->contype );

        if( ConstraintNode->pktable != NULL ){
          reference_table_name = ConstraintNode->pktable->relname;
          reference_table_names.push_back( reference_table_name );
        }

        // Get constraint name
        if( ConstraintNode->conname != NULL)
          conname = ConstraintNode->conname;

        catalog::Constraint* constraint = new catalog::Constraint( contype, conname, reference_table_name );
        column_constraints.push_back(*constraint);
      }
    }// end of parsing constraint

    catalog::ColumnInfo *column_info = new catalog::ColumnInfo( column_valueType,
                                                                column_length,
                                                                column_name,
                                                                column_constraints);

    // Insert column_info into ColumnInfos
    column_infos.push_back(*column_info);
  }// end of parsing column list

  return column_infos;
}

/**
 * @brief Construct IndexInfo from a index statement
 * @param Istmt an index statement
 * @return IndexInfo
 */
IndexInfo* DDL::ConstructIndexInfoByParsingIndexStmt( IndexStmt* Istmt ){
  std::string index_name;
  std::string table_name;
  IndexMethodType method_type;
  IndexType type;
  bool unique_keys;
  std::vector<std::string> key_column_names;

  // Table name
  table_name = Istmt->relation->relname;

  // Key column names
  ListCell   *entry;
  foreach(entry, Istmt->indexParams){
    IndexElem *indexElem = lfirst(entry);
    if( indexElem->name != NULL ){
      key_column_names.push_back(indexElem->name);
    }
  }

  // Index name and index type
  if( Istmt->idxname == NULL && Istmt->isconstraint ){
    if( Istmt->primary ) {
      index_name = table_name+"_pkey";
      type = INDEX_TYPE_PRIMARY_KEY;
    }else if( Istmt->unique ){
      index_name = table_name;
      for( auto column_name : key_column_names ){
        index_name += "_"+column_name+"_";
      }
        index_name += "key";
      type = INDEX_TYPE_UNIQUE;
    }
  }else{
    type = INDEX_TYPE_NORMAL;
  }

  // Index method type
  // TODO :: More access method types need
  method_type = INDEX_METHOD_TYPE_BTREE_MULTIMAP;

  IndexInfo* index_info = new IndexInfo( index_name,
                                         table_name,
                                         method_type,
                                         type,
                                         Istmt->unique,
                                         key_column_names);
  return index_info;
}

/**
 * @brief Create table.
 * @param table_name Table name
 * @param column_infos Information about the columns
 * @param schema Schema for the table
 * @return true if we created a table, false otherwise
 */
bool DDL::CreateTable( std::string table_name,
                        std::vector<catalog::ColumnInfo> column_infos,
                        catalog::Schema *schema){

  //===--------------------------------------------------------------------===//
  // Check Parameters
  //===--------------------------------------------------------------------===//
  assert( !table_name.empty() );
  assert( column_infos.size() > 0 || schema != NULL );

  Oid database_oid = GetCurrentDatabaseOid();
  if(database_oid == InvalidOid)
    return false;

  // Construct our schema from vector of ColumnInfo
  if( schema == NULL)
    schema = new catalog::Schema(column_infos);

  // FIXME: Construct table backend
  storage::VMBackend *backend = new storage::VMBackend();

  // Build a table from schema
  storage::DataTable *table = storage::TableFactory::GetDataTable(database_oid, schema, table_name);
  catalog::Schema* our_schema = table->GetSchema();

  /* DEBUGGING */
  //std::cout << "table name : " << table_name << "\n" << *our_schema << std::endl;

  // In Postgres, indexes such as primary key index, unique indexy are created before table.
  // In Peloton, however, the table is required to create an index. For that reason, index information
  // was stored before and now it will be created.
  for( auto index_info : index_infos){
    bool status;
    status = peloton::bridge::DDL::CreateIndex( index_info.GetIndexName(),
        index_info.GetTableName(),
        index_info.GetMethodType(),
        index_info.GetType(),
        index_info.IsUnique(),
        index_info.GetKeyColumnNames());
    fprintf(stderr, "DDLCreateIndex %s :: %d \n", index_info.GetIndexName().c_str(), status);
  }
  index_infos.clear();

 if(table != nullptr) {
    LOG_INFO("Created table : %s", table_name.c_str());
    return true;
  }

  return false;
}

/**
 * @brief Drop table.
 * @param table_oid Table id.
 * @return true if we dropped the table, false otherwise
 */
bool DDL::DropTable(Oid table_oid) {

  oid_t database_oid = GetCurrentDatabaseOid();

  if(database_oid == InvalidOid || table_oid == InvalidOid) {
    LOG_WARN("Could not drop table :: db oid : %u table oid : %u", database_oid, table_oid);
    return false;
  }

  bool status = storage::TableFactory::DropDataTable(database_oid, table_oid);
  if(status == true) {
    LOG_INFO("Dropped table with oid : %u\n", table_oid);
    return true;
  }

  return false;
}

/**
 * @brief Create index.
 * @param index_name Index name
 * @param table_name Table name
 * @param type Type of the index
 * @param unique Index is unique or not ?
 * @param key_column_names column names for the key table
 * @return true if we create the index, false otherwise
 */
bool DDL::CreateIndex(std::string index_name,
                      std::string table_name,
                      IndexMethodType  index_method_type,
                      IndexType  index_type,
                      bool unique_keys,
                      std::vector<std::string> key_column_names,
                      bool bootstrap ){

  assert( !index_name.empty() );
  assert( !table_name.empty() );
  assert( key_column_names.size() > 0  );

  // NOTE: We currently only support btree as our index implementation
  // TODO : Support other types based on "type" argument
  IndexMethodType our_index_type = INDEX_METHOD_TYPE_BTREE_MULTIMAP;

  // Get the database oid and table oid
  oid_t database_oid = GetCurrentDatabaseOid();
  assert( database_oid );

  //oid_t table_oid = GetRelationOid(table_name.c_str());

  // Get the table location from manager
  auto table = catalog::Manager::GetInstance().GetLocation(database_oid, table_name);
  storage::DataTable* data_table = (storage::DataTable*) table;
  auto tuple_schema = data_table->GetSchema();

  // Construct key schema
  std::vector<oid_t> key_columns;

  // Based on the key column info, get the oid of the given 'key' columns in the tuple schema
  for( auto key_column_name : key_column_names ){
    for( oid_t tuple_schema_column_itr = 0; tuple_schema_column_itr < tuple_schema->GetColumnCount();
        tuple_schema_column_itr++){

      // Get the current column info from tuple schema
      catalog::ColumnInfo column_info = tuple_schema->GetColumnInfo(tuple_schema_column_itr);
      // Compare Key Schema's current column name and Tuple Schema's current column name
      if( key_column_name == column_info.name ){
        key_columns.push_back(tuple_schema_column_itr);

        // TODO :: Need to talk with Joy
        // NOTE :: Since pg_attribute doesn't have any information about primary key and unique key,
        //         I try to store these information when we create an unique and primary key index
        if( bootstrap ){
          if( index_type == INDEX_TYPE_PRIMARY_KEY ){
            catalog::Constraint* constraint = new catalog::Constraint( CONSTRAINT_TYPE_PRIMARY );
            tuple_schema->AddConstraintInColumn( tuple_schema_column_itr, constraint);
          }else if( index_type == INDEX_TYPE_UNIQUE ){
            catalog::Constraint* constraint = new catalog::Constraint( CONSTRAINT_TYPE_UNIQUE );
            tuple_schema->AddConstraintInColumn( tuple_schema_column_itr, constraint);
          }
        }

      }
    }
  }

  auto key_schema = catalog::Schema::CopySchema(tuple_schema, key_columns);

  // Create index metadata and physical index
  index::IndexMetadata* metadata = new index::IndexMetadata(index_name, our_index_type,
                                                            tuple_schema, key_schema,
                                                            unique_keys);
  index::Index* index = index::IndexFactory::GetInstance(metadata);

  // Record the built index in the table
  switch( index_type ){
  case INDEX_TYPE_NORMAL:
	  data_table->AddIndex(index);
	  break;
  case INDEX_TYPE_PRIMARY_KEY:
	  data_table->SetPrimaryIndex(index);
	  break;
  case INDEX_TYPE_UNIQUE:
	  data_table->AddUniqueIndex(index);
	  break;
  default:
      elog(LOG, "unrecognized index type: %d", index_type);
  }

  return true;
}

} // namespace bridge
} // namespace peloton
<|endoftext|>


<commit_before>// ParallelExecutionOfCommands.cpp :
// This aplication aims to provide functionality for parallel execution of Behat commands.
//

//#include "stdafx.h"
#include <iostream>
#include <string>
#include <cstring>
#include <stdlib.h>
//#include <unistd.h>
#include <thread>

using namespace std;

int main()
{
	// Declaration of needed variables.
	int processes_Count;
	char command[128];
	string behat_Profile;
	string behat_Feature_Folder;
	string behat_Bin = "bin/behat -p ";
	string result_Command_String;
	thread thread_Array[100];
	bool parallel_Execution = true;

	// Entering values for the variables.
	cout << "Enter number of processes you want to execute: ";
	cin >> processes_Count;
	cout << "Enter Behat profile you want to use: ";
	cin >> behat_Profile;
	cout << "Enter Behat feature folder or file location you want to execute: ";
	cin >> behat_Feature_Folder;

	// Creting command for execution.
	result_Command_String = behat_Bin + behat_Profile + " features/" + behat_Feature_Folder;
	strcpy(command, result_Command_String.c_str());

	cout << "You will execute " << processes_Count << " processes with the following command: " << command << ".\n";

	// Creating threads to handle the command execution.
	for (size_t i = 0; i < processes_Count; i++)
	{
		thread_Array[i] = thread(system, command);
		thread_Array[i].join();
	}
	for (size_t i = 0; i < sizeof(thread_Array); i++)
	{
		thread_Array[i].detach();
	}

	//// Creating process for execution of the command.
	//pid_t pids[processes_Count];
	//for (size_t i = 0; i < processes_Count; i++)
	//{
	//	if ((pids[i] = fork()) < 0)
	//	{
	//		perror("Process was unable to be created.");
	//		abort();
	//	}
	//	else if (pids[i] == 0)
	//	{
	//		system(command);
	//		exit(0);
	//	}
	//}

    return 0;
}

<commit_msg>tesing.<commit_after>// ParallelExecutionOfCommands.cpp :
// This aplication aims to provide functionality for parallel execution of Behat commands.
//

//#include "stdafx.h"
#include <iostream>
#include <string>
#include <cstring>
#include <stdlib.h>
//#include <unistd.h>
#include <thread>

using namespace std;

int main()
{
	// Declaration of needed variables.
	int processes_Count;
	char command[128];
	string behat_Profile;
	string behat_Feature_Folder;
	string behat_Bin = "bin/behat -p ";
	string result_Command_String;
	thread thread_Array[100];
	bool parallel_Execution = true;

	// Entering values for the variables.
	cout << "Enter number of processes you want to execute: ";
	cin >> processes_Count;
	cout << "Enter Behat profile you want to use: ";
	cin >> behat_Profile;
	cout << "Enter Behat feature folder or file location you want to execute: ";
	cin >> behat_Feature_Folder;

	// Creting command for execution.
	result_Command_String = behat_Bin + behat_Profile + " features/" + behat_Feature_Folder;
	strcpy(command, result_Command_String.c_str());

	cout << "You will execute " << processes_Count << " processes with the following command: " << command << ".\n";

	// Creating threads to handle the command execution.
	for (size_t i = 0; i < processes_Count; i++)
	{
		thread_Array[i] = thread(system, command);

	}
	for (size_t i = 0; i < processes_Count; i++)
	{
		thread_Array[i].join();
	}

	//// Creating process for execution of the command.
	//pid_t pids[processes_Count];
	//for (size_t i = 0; i < processes_Count; i++)
	//{
	//	if ((pids[i] = fork()) < 0)
	//	{
	//		perror("Process was unable to be created.");
	//		abort();
	//	}
	//	else if (pids[i] == 0)
	//	{
	//		system(command);
	//		exit(0);
	//	}
	//}

    return 0;
}

<|endoftext|>


<commit_before>// BenchmarkRegex.cpp : Defines the entry point for the console application.
//

#include <vector>
#include <iostream>
#include <iomanip>
#include <string>
#include <cstring>
#include <cmath>
#include <cassert>
#include <sstream>
#include <cstdlib>
#include <chrono>
#include <regex>
#include <map>
#include <thread>
#include <mutex>

class timer
{
public:
	timer() = default;
	void start_timing(const std::string& text_)
	{
		text = text_;
		begin = std::chrono::high_resolution_clock::now();
	}
	void stop_timing()
	{
		auto end = std::chrono::high_resolution_clock::now();
		auto dur = end - begin;
		auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(dur).count();
		std::cout << std::setw(16) << text << ":" << std::setw(5) << ms << "ms" << std::endl;
	}

private:
	std::string text;
	std::chrono::steady_clock::time_point begin;
};

#ifdef WIN32

#pragma optimize("", off)
template <class T>
void do_not_optimize_away(T* datum) {
	datum = datum;
}
#pragma optimize("", on)

#else
static void do_not_optimize_away(void* p) {
	asm volatile("" : : "g"(p) : "memory");
}
#endif

struct singleton
{
public:
	static void set(const std::regex* obj_ptr) { s_regex_ptr = obj_ptr; }
	static const std::regex* get()
	{
		std::lock_guard<std::mutex> lock(s_mutex);

		std::thread::id thread_id = std::this_thread::get_id();
		std::map<std::thread::id, const std::regex*>::iterator it = s_map.find(thread_id);
		if (it == s_map.end())
		{
			const std::regex* ptr = new std::regex(*s_regex_ptr);
			s_map[thread_id] = ptr;
			return ptr;
		}
		return it->second;
	}
private:
	static std::map<std::thread::id, const std::regex*> s_map;
	static const std::regex* s_regex_ptr;
	static std::mutex s_mutex;
};
std::map<std::thread::id, const std::regex*> singleton::s_map;
const std::regex* singleton::s_regex_ptr = nullptr;
std::mutex singleton::s_mutex;

const std::string local_match(const std::string& text);
const std::string static_match(const std::string& text); // not reentrant-safe
const std::string singleton_match(const std::string& text);

int main(int argc, char* argv[])
{
	const int LOOP = 100000;
	std::string str1 = "Zoomer PRICE: HK$1.23 PER SHARE";
	std::string str2 = "Boomer PRICE: HK$4.56 PER SHARE";

	std::vector<std::string> vec;
	vec.push_back(str1);
	vec.push_back(str2);

	timer stopwatch;

	stopwatch.start_timing("local regex object");
	for(int j = 0; j < LOOP; ++j)
	{
		for(size_t i = 0; i < vec.size(); ++i)
		{
			do_not_optimize_away(local_match(vec[i]).c_str());
		}
	}
	stopwatch.stop_timing();

	stopwatch.start_timing("static regex object");
	for(int j = 0; j < LOOP; ++j)
	{
		for(size_t i = 0; i < vec.size(); ++i)
		{
			do_not_optimize_away(static_match(vec[i]).c_str());
		}
	}
	stopwatch.stop_timing();

	const std::regex regex(".*PRICE:.*HK\\$(\\d+\\.\\d+|[-+]*\\d+).*PER SHARE");
	singleton::set(&regex);

	stopwatch.start_timing("singleton regex object");
	for (int j = 0; j < LOOP; ++j)
	{
		for (size_t i = 0; i < vec.size(); ++i)
		{
			do_not_optimize_away(singleton_match(vec[i]).c_str());
		}
	}
	stopwatch.stop_timing();

	/*
	std::cout << local_match(str1) << std::endl;
	std::cout << local_match(str2) << std::endl;
	std::cout << static_match(str1) << std::endl;
	std::cout << static_match(str2) << std::endl;
	std::cout << singleton_match(str1) << std::endl;
	std::cout << singleton_match(str2) << std::endl;
	*/

	return 0;
}

const std::string local_match(const std::string& text)
{
	std::string ipo_price = "";
	std::smatch what;
	const std::regex regex(".*PRICE:.*HK\\$(\\d+\\.\\d+|[-+]*\\d+).*PER SHARE");
	if (std::regex_match(text, what, regex))
	{
		ipo_price = what[1];
	}
	return ipo_price;
}

const std::string static_match(const std::string& text) // not reentrant-safe
{
	std::string ipo_price = "";
	std::smatch what;
	static const std::regex regex(".*PRICE:.*HK\\$(\\d+\\.\\d+|[-+]*\\d+).*PER SHARE");
	if (std::regex_match(text, what, regex))
	{
		ipo_price = what[1];
	}
	return ipo_price;
}

const std::string singleton_match(const std::string& text)
{
	std::string ipo_price = "";
	std::smatch what;
	const std::regex* regex_ptr = singleton::get();
	if (std::regex_match(text, what, *regex_ptr))
	{
		ipo_price = what[1];
	}
	return ipo_price;
}
<commit_msg>Add factory class<commit_after>// BenchmarkRegex.cpp : Defines the entry point for the console application.
//

#include <vector>
#include <iostream>
#include <iomanip>
#include <string>
#include <cstring>
#include <cmath>
#include <cassert>
#include <sstream>
#include <cstdlib>
#include <chrono>
#include <regex>
#include <unordered_map>
#include <thread>
#include <mutex>
#include <memory>

class timer
{
public:
	timer() = default;
	void start_timing(const std::string& text_)
	{
		text = text_;
		begin = std::chrono::high_resolution_clock::now();
	}
	void stop_timing()
	{
		auto end = std::chrono::high_resolution_clock::now();
		auto dur = end - begin;
		auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(dur).count();
		std::cout << std::setw(16) << text << ":" << std::setw(5) << ms << "ms" << std::endl;
	}

private:
	std::string text;
	std::chrono::steady_clock::time_point begin;
};

#ifdef WIN32

#pragma optimize("", off)
template <class T>
void do_not_optimize_away(T* datum) {
	datum = datum;
}
#pragma optimize("", on)

#else
static void do_not_optimize_away(void* p) {
	asm volatile("" : : "g"(p) : "memory");
}
#endif

struct singleton
{
public:
	typedef std::unordered_map<std::thread::id, std::unique_ptr<std::regex> > MapType;
	static void set(const std::string& reg_exp) { s_reg_exp = reg_exp; }
	static const std::regex& get()
	{
		std::lock_guard<std::mutex> lock(s_mutex);

		std::thread::id thread_id = std::this_thread::get_id();
		MapType::iterator it = s_map.find(thread_id);
		if (it == s_map.end())
		{
			std::unique_ptr<char[]> p = std::unique_ptr<char[]>(new char[1000]);
			s_map[thread_id] = std::unique_ptr<std::regex>(new std::regex(s_reg_exp));
			return *s_map[thread_id];
		}
		return *(it->second);
	}
private:
	static MapType s_map;
	static std::string s_reg_exp;
	static std::mutex s_mutex;
};
singleton::MapType singleton::s_map;
std::string singleton::s_reg_exp;
std::mutex singleton::s_mutex;

struct factory
{
public:
	static std::unique_ptr<std::regex> get(const std::string& reg_exp)
	{
		std::lock_guard<std::mutex> lock(s_mutex);
		std::unique_ptr<char[]> p = std::unique_ptr<char[]>(new char[1000]);
		return std::unique_ptr<std::regex>(new std::regex(reg_exp));
	}
private:
	static std::mutex s_mutex;
};
std::mutex factory::s_mutex;

const std::string local_match(const std::string& text);
const std::string static_match(const std::string& text); // not reentrant-safe
const std::string singleton_match(const std::string& text);
const std::string factory_match(const std::string& text, const std::regex& regex);

void parallel_invoke(int size, int threads, std::function<void(int, int)> func)
{
	typedef std::unique_ptr<std::thread> PtrType;
	std::vector< PtrType > vec;
	int each = size / threads;

	for (int i = 0; i < threads; ++i)
	{
		if (i == threads - 1) // last thread
		{
			vec.emplace_back(PtrType(new std::thread(func, each*i, each*(i + 1) + (size % threads))));
		}
		else
		{
			vec.emplace_back(PtrType(new std::thread(func, each*i, each*(i + 1) )));
		}
	}

	for (size_t i = 0; i < vec.size(); ++i)
	{
		vec[i]->join();
	}
}

const std::string REG_EXP = ".*PRICE:.*US\\$(\\d+\\.\\d+|[-+]*\\d+).*PER SHARE";

int main(int argc, char* argv[])
{
	const int LOOP = 1000000;
	std::string str1 = "Zoomer PRICE: US$1.23 PER SHARE";
	std::string str2 = "Boomer PRICE: US$4.56 PER SHARE";

	std::vector<std::string> vec;
	vec.push_back(str1);
	vec.push_back(str2);

	timer stopwatch;

	stopwatch.start_timing("local regex object");
	for(int j = 0; j < LOOP; ++j)
	{
		for(size_t i = 0; i < vec.size(); ++i)
		{
			do_not_optimize_away(local_match(vec[i]).c_str());
		}
	}
	stopwatch.stop_timing();

	stopwatch.start_timing("static regex object");
	for(int j = 0; j < LOOP; ++j)
	{
		for(size_t i = 0; i < vec.size(); ++i)
		{
			do_not_optimize_away(static_match(vec[i]).c_str());
		}
	}
	stopwatch.stop_timing();

	singleton::set(REG_EXP);
	stopwatch.start_timing("singleton regex object");
	for (int j = 0; j < LOOP; ++j)
	{
		for (size_t i = 0; i < vec.size(); ++i)
		{
			do_not_optimize_away(singleton_match(vec[i]).c_str());
		}
	}
	stopwatch.stop_timing();

	stopwatch.start_timing("local regex object(4 threads)");
	parallel_invoke(LOOP, 4, [&vec](int start, int end) {
		for (int j = start; j < end; ++j)
		{
			for (size_t i = 0; i < vec.size(); ++i)
			{
				do_not_optimize_away(local_match(vec[i]).c_str());
			}
		}
	});
	stopwatch.stop_timing();

	stopwatch.start_timing("singleton regex object(4 threads)");
	parallel_invoke(LOOP, 4, [&vec] (int start, int end) {
		for (int j = start; j < end; ++j)
		{
			for (size_t i = 0; i < vec.size(); ++i)
			{
				do_not_optimize_away(singleton_match(vec[i]).c_str());
			}
		}
	});
	stopwatch.stop_timing();

	stopwatch.start_timing("factory regex object(4 threads)");
	parallel_invoke(LOOP, 4, [&vec](int start, int end) {
		std::unique_ptr<std::regex> ptr = factory::get(REG_EXP);
		const std::regex& regex = *ptr;

		for (int j = start; j < end; ++j)
		{
			for (size_t i = 0; i < vec.size(); ++i)
			{
				do_not_optimize_away(factory_match(vec[i], regex).c_str());
			}
		}
	});
	stopwatch.stop_timing();

	/*
	std::cout << local_match(str1) << std::endl;
	std::cout << local_match(str2) << std::endl;
	std::cout << static_match(str1) << std::endl;
	std::cout << static_match(str2) << std::endl;
	singleton::set(REG_EXP);
	std::cout << singleton_match(str1) << std::endl;
	std::cout << singleton_match(str2) << std::endl;
	const std::regex regex(REG_EXP);
	std::cout << factory_match(str1, regex) << std::endl;
	std::cout << factory_match(str2, regex) << std::endl;
	*/

	return 0;
}

const std::string local_match(const std::string& text)
{
	std::string ipo_price = "";
	std::smatch what;
	const std::regex regex(REG_EXP);
	if (std::regex_match(text, what, regex))
	{
		ipo_price = what[1];
	}
	return ipo_price;
}

const std::string static_match(const std::string& text) // not reentrant-safe
{
	std::string ipo_price = "";
	std::smatch what;
	static const std::regex regex(REG_EXP);
	if (std::regex_match(text, what, regex))
	{
		ipo_price = what[1];
	}
	return ipo_price;
}

const std::string singleton_match(const std::string& text)
{
	std::string ipo_price = "";
	std::smatch what;
	const std::regex& regex = singleton::get();
	if (std::regex_match(text, what, regex))
	{
		ipo_price = what[1];
	}
	return ipo_price;
}

const std::string factory_match(const std::string& text, const std::regex& regex)
{
	std::string ipo_price = "";
	std::smatch what;
	if (std::regex_match(text, what, regex))
	{
		ipo_price = what[1];
	}
	return ipo_price;
}
<|endoftext|>


<commit_before>// MolarMass.cpp
// This program should loop through the csv file Atomic Masses.csv and sum the masses of elements
#include <iostream>
#include <fstream>
#include <string>
#include <stdlib.h>
//#include <sstream>

int main()
{
	string line; // Holds the line in the file
	string element; //The variable for the element being searched

	ofstream myfile ("AtomicMasses.csv"); // Opens the file

	if (myfile.is_open())
	{
		while (myfile.good()) // The resources I found online had this. Not quite sure of the nuances, but it seems to work
		{
			getline(myfile, line);// Gets the current line in the file
			if (element == line)
			{
				//Find a way to search through the different columns of a csv file for the atomic mass
			}
		}
	}

		myfile.close();

	else
	{
		std::cout << "Unable to open file\n";
	}

	return 0;
}
<commit_msg>Update MolarMass.cpp<commit_after>// MolarMass.cpp
// This program should loop through the csv file Atomic Masses.csv and sum the masses of elements
#include <iostream>
#include <fstream>
#include <string>
#include <stdlib.h>
#include <conio.h>
//#include <sstream>

int main()
{
	std::string line; // Holds the line in the file
	std::string element; //The variable for the element being searched

	//This temporarily allows for an element to be entered for testing.
	std::cin >> element;
	//This should be removed after testing

	ofstream myfile ("AtomicMasses.csv"); //XKCD Undelclared Identifier

	if (myfile.is_open())
	{
		while (myfile.good()) // The resources I found online had this. Not quite sure of the nuances, but it seems to work
		{
			getline(myfile, line);
			if (std::string::find(element) != 0);
			{
				//Function that finds a certain element in the line (in this case, the third)
				std::string::columnValue;
					for (i = 1, i <= 3, i++);
				{
					std::string::find(",");

				}

			}
		}
	}
	else
	{
		std::cout << "Unable to open file\n";
	}
		myfile.close(); //Closes the file

	_getch();

	return 0;
<|endoftext|>


<commit_before>#include "MoveDummy.h"
#include "ObjectDummy.h"
#include "BodyDummy.h"
#include "ShapeCapsule.h"

#include "Utils.h"
#include "Engine.h"
#include "Physics.h"
#include "Game.h"
#include "World.h"

#define MOVE_DUMMY_IFPS			(1.0f/100.0f)
#define MOVE_DUMMY_CLAMP           89.9f
#define MOVE_DUMMY_COLLISIONS		1

using namespace MathLib;

CMoveDummy::CMoveDummy()		//캯
{
	m_pObject = new CObjectDummy();		//һʵ
	m_pDummy = new CBodyDummy();
	m_pShape = new CShapeCapsule(0.5f, 1.0f);

	SetCollisionMask(1);
	Clear();
}

CMoveDummy::~CMoveDummy()
{
	m_pDummy->SetObject(NULL);
	SAFE_DELETE(m_pObject);
	SAFE_DELETE(m_pDummy);
}

void CMoveDummy::SetCollision(int c)
{
	m_nCollision = c;
}

int CMoveDummy::GetCollision() const
{
	return m_nCollision;
}

void CMoveDummy::SetCollisionMask(int m)
{
	m_pShape->SetCollisionMask(m);
}

int CMoveDummy::GetCollisionMask() const
{
	return m_nCollisionMask;
}

void CMoveDummy::SetGround(int g)
{
	m_nGround = g;
}

int CMoveDummy::GetGround() const
{
	return m_nGround;
}

void CMoveDummy::SetCeiling(int c)
{
	m_nCeiling = c;
}

int CMoveDummy::GetCeiling() const
{
	return m_nCeiling;
}

void CMoveDummy::SetPosition(const MathLib::vec3& p)
{
	m_vPosition = p;
}

const MathLib::vec3& CMoveDummy::GetPosition() const
{
	return m_vPosition;
}

void CMoveDummy::SetCollisionRadius(float radius)
{
	if (!Compare(m_pShape->GetRadius(), radius))
	{
		m_pDummy->SetPreserveTransform(Mat4(Translate(m_vUp * (radius - m_pShape->GetRadius()))) * m_pDummy->GetTransform());
		m_pShape->SetRadius(radius);
	}

	Update_Bounds();
}

float CMoveDummy::GetCollisionRadius() const
{
	return m_pShape->GetRadius();
}

void CMoveDummy::SetCollisionHeight(float height)
{
	if (!Compare(m_pShape->GetHeight(), height))
	{
		m_pDummy->SetPreserveTransform(Mat4(Translate(m_vUp * (height - m_pShape->GetHeight()) * 0.5f)) * m_pDummy->GetTransform());
		m_pShape->SetHeight(height);
	}

	Update_Bounds();
}
float CMoveDummy::GetCollisionHeight() const
{
	return m_pShape->GetHeight();
}

void CMoveDummy::SetUp(const MathLib::vec3& u)
{
	m_vUp = u;
}

const MathLib::vec3& CMoveDummy::GetUp() const
{
	return m_vUp;
}
void CMoveDummy::SetEnabled(int e)
{
	m_nEnabled = e;
}

float CMoveDummy::GetCollisionHeight() const
{
	return m_pShape->GetHeight();
}
void CMoveDummy::SetUp(const MathLib::vec3& u)
{
	m_vUp = u;
}

const MathLib::vec3& CMoveDummy::GetUp() const
{
	return m_vUp;
}
void CMoveDummy::SetEnabled(int e)
{
	m_nEnabled = e;
}

int CMoveDummy::GetEnabled() const
{
	return m_nEnabled;
}

void CMoveDummy::SetVelocity(const MathLib::vec3& v)
{
	m_vVelocity = v;
}

const MathLib::vec3& CMoveDummy::GetVelocity() const
{
	return m_vVelocity;
}

void CMoveDummy::SetMaxVelocity(float v)
{
	m_fMaxVelocity = v;
}
float CMoveDummy::GetMaxVelocity() const
{
	return m_fMaxVelocity;
}

void CMoveDummy::SetAcceleration(float a)
{
	m_fAcceleration = a;
}
float CMoveDummy::GetAcceleration() const
{
	return m_fAcceleration;
}
float CMoveDummy::GetMaxThrough() const
{
	return m_fMaxThrough;
}
void CMoveDummy::Clear()
{
	SetVelocity(vec3_zero);
	SetMaxVelocity(2.5f);
	SetAcceleration(15.0f);
	SetCollision(1);
	SetCollisionMask(1);
	SetGround(0);
	SetCeiling(0);
	SetPosition(vec3_zero);
	SetUp(vec3(0.0f, 0.0f, 1.0f));
	SetEnabled(1);
	m_pDummy->SetEnabled(1);
	m_pObject->SetBody(NULL);
	m_pObject->SetBody(m_pDummy);

	m_pShape->SetBody(NULL);
	m_pShape->SetBody(m_pDummy);
	m_pShape->SetExclusionMask(2);

	m_pObject->SetWorldTransform(Get_Body_Transform());

	SetCollisionRadius(0.5f);
	SetCollisionHeight(1.0f);

	SetMaxThrough(0.4f);

	m_vecContacts.Clear();

}
<commit_msg>Signed-off-by: mrlitong <litongtongxue@gmail.com><commit_after>#include "MoveDummy.h"
#include "ObjectDummy.h"
#include "BodyDummy.h"
#include "ShapeCapsule.h"

#include "Utils.h"
#include "Engine.h"
#include "Physics.h"
#include "Game.h"
#include "World.h"

#define MOVE_DUMMY_IFPS			(1.0f/100.0f)
#define MOVE_DUMMY_CLAMP           89.9f
#define MOVE_DUMMY_COLLISIONS		1

using namespace MathLib;

CMoveDummy::CMoveDummy()		//캯
{
	m_pObject = new CObjectDummy();		//һʵ
	m_pDummy = new CBodyDummy();
	m_pShape = new CShapeCapsule(0.5f, 1.0f);

	SetCollisionMask(1);
	Clear();
}

CMoveDummy::~CMoveDummy()
{
	m_pDummy->SetObject(NULL);
	SAFE_DELETE(m_pObject);
	SAFE_DELETE(m_pDummy);
}

void CMoveDummy::SetCollision(int c)
{
	m_nCollision = c;
}

int CMoveDummy::GetCollision() const
{
	return m_nCollision;
}

void CMoveDummy::SetCollisionMask(int m)
{
	m_pShape->SetCollisionMask(m);
}

int CMoveDummy::GetCollisionMask() const
{
	return m_nCollisionMask;
}

void CMoveDummy::SetGround(int g)
{
	m_nGround = g;
}

int CMoveDummy::GetGround() const
{
	return m_nGround;
}

void CMoveDummy::SetCeiling(int c)
{
	m_nCeiling = c;
}

int CMoveDummy::GetCeiling() const
{
	return m_nCeiling;
}

void CMoveDummy::SetPosition(const MathLib::vec3& p)
{
	m_vPosition = p;
}

const MathLib::vec3& CMoveDummy::GetPosition() const
{
	return m_vPosition;
}

void CMoveDummy::SetCollisionRadius(float radius)
{
	if (!Compare(m_pShape->GetRadius(), radius))
	{
		m_pDummy->SetPreserveTransform(Mat4(Translate(m_vUp * (radius - m_pShape->GetRadius()))) * m_pDummy->GetTransform());
		m_pShape->SetRadius(radius);
	}

	Update_Bounds();
}

float CMoveDummy::GetCollisionRadius() const
{
	return m_pShape->GetRadius();
}

void CMoveDummy::SetCollisionHeight(float height)
{
	if (!Compare(m_pShape->GetHeight(), height))
	{
		m_pDummy->SetPreserveTransform(Mat4(Translate(m_vUp * (height - m_pShape->GetHeight()) * 0.5f)) * m_pDummy->GetTransform());
		m_pShape->SetHeight(height);
	}

	Update_Bounds();
}
float CMoveDummy::GetCollisionHeight() const
{
	return m_pShape->GetHeight();
}

void CMoveDummy::SetUp(const MathLib::vec3& u)
{
	m_vUp = u;
}

const MathLib::vec3& CMoveDummy::GetUp() const
{
	return m_vUp;
}
void CMoveDummy::SetEnabled(int e)
{
	m_nEnabled = e;
}

float CMoveDummy::GetCollisionHeight() const
{
	return m_pShape->GetHeight();
}
void CMoveDummy::SetUp(const MathLib::vec3& u)
{
	m_vUp = u;
}

const MathLib::vec3& CMoveDummy::GetUp() const
{
	return m_vUp;
}
void CMoveDummy::SetEnabled(int e)
{
	m_nEnabled = e;
}

int CMoveDummy::GetEnabled() const
{
	return m_nEnabled;
}

void CMoveDummy::SetVelocity(const MathLib::vec3& v)
{
	m_vVelocity = v;
}

const MathLib::vec3& CMoveDummy::GetVelocity() const
{
	return m_vVelocity;
}

void CMoveDummy::SetMaxVelocity(float v)
{
	m_fMaxVelocity = v;
}
float CMoveDummy::GetMaxVelocity() const
{
	return m_fMaxVelocity;
}

void CMoveDummy::SetAcceleration(float a)
{
	m_fAcceleration = a;
}
float CMoveDummy::GetAcceleration() const
{
	return m_fAcceleration;
}
float CMoveDummy::GetMaxThrough() const
{
	return m_fMaxThrough;
}
void CMoveDummy::Clear()
{
	SetVelocity(vec3_zero);
	SetMaxVelocity(2.5f);
	SetAcceleration(15.0f);
	SetCollision(1);
	SetCollisionMask(1);
	SetGround(0);
	SetCeiling(0);
	SetPosition(vec3_zero);
	SetUp(vec3(0.0f, 0.0f, 1.0f));
	SetEnabled(1);
	m_pDummy->SetEnabled(1);
	m_pObject->SetBody(NULL);
	m_pObject->SetBody(m_pDummy);

	m_pShape->SetBody(NULL);
	m_pShape->SetBody(m_pDummy);
	m_pShape->SetExclusionMask(2);

	m_pObject->SetWorldTransform(Get_Body_Transform());

	SetCollisionRadius(0.5f);
	SetCollisionHeight(1.0f);

	SetMaxThrough(0.4f);

	m_vecContacts.Clear();

}
int CMoveDummy::Update(float fIfps, const MathLib::vec3& vDirection)
{
	return Update(fIfps, vDirection, m_vPosition);
}<|endoftext|>


<commit_before>/*
 * Copyright (C) 2020 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.
 */

#include "src/trace_processor/dynamic/thread_state_generator.h"

#include <memory>
#include <set>

#include "src/trace_processor/containers/row_map.h"
#include "src/trace_processor/types/trace_processor_context.h"

namespace perfetto {
namespace trace_processor {

ThreadStateGenerator::ThreadStateGenerator(TraceProcessorContext* context)
    : running_string_id_(context->storage->InternString("Running")),
      runnable_string_id_(context->storage->InternString("R")),
      context_(context) {}

ThreadStateGenerator::~ThreadStateGenerator() = default;

util::Status ThreadStateGenerator::ValidateConstraints(
    const QueryConstraints&) {
  return util::OkStatus();
}

std::unique_ptr<Table> ThreadStateGenerator::ComputeTable(
    const std::vector<Constraint>&,
    const std::vector<Order>&) {
  if (!unsorted_thread_state_table_) {
    int64_t trace_end_ts =
        context_->storage->GetTraceTimestampBoundsNs().second;

    unsorted_thread_state_table_ = ComputeThreadStateTable(trace_end_ts);

    // We explicitly sort by ts here as ComputeThreadStateTable does not insert
    // rows in sorted order but we expect our clients to always want to sort
    // on ts. Writing ComputeThreadStateTable to insert in sorted order is
    // more trouble than its worth.
    sorted_thread_state_table_ = unsorted_thread_state_table_->Sort(
        {unsorted_thread_state_table_->ts().ascending()});
  }
  PERFETTO_CHECK(sorted_thread_state_table_);
  return std::unique_ptr<Table>(new Table(sorted_thread_state_table_->Copy()));
}

std::unique_ptr<tables::ThreadStateTable>
ThreadStateGenerator::ComputeThreadStateTable(int64_t trace_end_ts) {
  std::unique_ptr<tables::ThreadStateTable> table(new tables::ThreadStateTable(
      context_->storage->mutable_string_pool(), nullptr));

  const auto& raw_sched = context_->storage->sched_slice_table();
  const auto& instants = context_->storage->instant_table();

  // In both tables, exclude utid == 0 which represents the idle thread.
  Table sched = raw_sched.Filter({raw_sched.utid().ne(0)},
                                 RowMap::OptimizeFor::kLookupSpeed);
  Table waking = instants.Filter(
      {instants.name().eq("sched_waking"), instants.ref().ne(0)},
      RowMap::OptimizeFor::kLookupSpeed);

  // We prefer to use waking if at all possible and fall back to wakeup if not
  // available.
  if (waking.row_count() == 0) {
    waking = instants.Filter(
        {instants.name().eq("sched_wakeup"), instants.ref().ne(0)},
        RowMap::OptimizeFor::kLookupSpeed);
  }

  Table sched_blocked_reason = instants.Filter(
      {instants.name().eq("sched_blocked_reason"), instants.ref().ne(0)},
      RowMap::OptimizeFor::kLookupSpeed);

  const auto& sched_ts_col = sched.GetTypedColumnByName<int64_t>("ts");
  const auto& waking_ts_col = waking.GetTypedColumnByName<int64_t>("ts");
  const auto& blocked_ts_col =
      sched_blocked_reason.GetTypedColumnByName<int64_t>("ts");

  uint32_t sched_idx = 0;
  uint32_t waking_idx = 0;
  uint32_t blocked_idx = 0;
  std::unordered_map<UniqueTid, ThreadSchedInfo> state_map;
  while (sched_idx < sched.row_count() || waking_idx < waking.row_count() ||
         blocked_idx < sched_blocked_reason.row_count()) {
    int64_t sched_ts = sched_idx < sched.row_count()
                           ? sched_ts_col[sched_idx]
                           : std::numeric_limits<int64_t>::max();
    int64_t waking_ts = waking_idx < waking.row_count()
                            ? waking_ts_col[waking_idx]
                            : std::numeric_limits<int64_t>::max();
    int64_t blocked_ts = blocked_idx < sched_blocked_reason.row_count()
                             ? blocked_ts_col[blocked_idx]
                             : std::numeric_limits<int64_t>::max();

    // We go through all tables, picking the earliest timestamp from any
    // to process that event.
    int64_t min_ts = std::min({sched_ts, waking_ts, blocked_ts});
    if (min_ts == sched_ts) {
      AddSchedEvent(sched, sched_idx++, state_map, trace_end_ts, table.get());
    } else if (min_ts == waking_ts) {
      AddWakingEvent(waking, waking_idx++, state_map);
    } else /* (min_ts == blocked_ts) */ {
      AddBlockedReasonEvent(sched_blocked_reason, blocked_idx++, state_map);
    }
  }

  // At the end, go through and flush any remaining pending events.
  for (const auto& utid_to_pending_info : state_map) {
    UniqueTid utid = utid_to_pending_info.first;
    const ThreadSchedInfo& pending_info = utid_to_pending_info.second;
    FlushPendingEventsForThread(utid, pending_info, table.get(), base::nullopt);
  }

  return table;
}

void ThreadStateGenerator::AddSchedEvent(
    const Table& sched,
    uint32_t sched_idx,
    std::unordered_map<UniqueTid, ThreadSchedInfo>& state_map,
    int64_t trace_end_ts,
    tables::ThreadStateTable* table) {
  int64_t ts = sched.GetTypedColumnByName<int64_t>("ts")[sched_idx];
  UniqueTid utid = sched.GetTypedColumnByName<uint32_t>("utid")[sched_idx];
  ThreadSchedInfo* info = &state_map[utid];

  // Due to races in the kernel, it is possible for the same thread to be
  // scheduled on different CPUs at the same time. This will manifest itself
  // here by having |info->desched_ts| in the future of this scheduling slice
  // (i.e. there was a scheduling slice in the past which ended after the start
  // of the current scheduling slice).
  //
  // We work around this problem by truncating the previous slice to the start
  // of this slice and not adding the descheduled slice (i.e. we don't call
  // |FlushPendingEventsForThread| which adds this slice).
  //
  // See b/186509316 for details and an example on when this happens.
  if (info->desched_ts && info->desched_ts.value() > ts) {
    uint32_t prev_sched_row = info->scheduled_row.value();
    int64_t prev_sched_start = table->ts()[prev_sched_row];

    // Just a double check that descheduling slice would have started at the
    // same time the scheduling slice would have ended.
    PERFETTO_DCHECK(prev_sched_start + table->dur()[prev_sched_row] ==
                    info->desched_ts.value());

    // Truncate the duration of the old slice to end at the start of this
    // scheduling slice.
    table->mutable_dur()->Set(prev_sched_row, ts - prev_sched_start);
  } else {
    FlushPendingEventsForThread(utid, *info, table, ts);
  }

  // Reset so we don't have any leftover data on the next round.
  *info = {};

  // Undo the expansion of the final sched slice for each CPU to the end of the
  // trace by setting the duration back to -1. This counteracts the code in
  // SchedEventTracker::FlushPendingEvents
  // TODO(lalitm): remove this hack when we stop expanding the last slice to the
  // end of the trace.
  int64_t dur = sched.GetTypedColumnByName<int64_t>("dur")[sched_idx];
  if (ts + dur == trace_end_ts) {
    dur = -1;
  }

  // Now add the sched slice itself as "Running" with the other fields
  // unchanged.
  tables::ThreadStateTable::Row sched_row;
  sched_row.ts = ts;
  sched_row.dur = dur;
  sched_row.cpu = sched.GetTypedColumnByName<uint32_t>("cpu")[sched_idx];
  sched_row.state = running_string_id_;
  sched_row.utid = utid;

  auto id_and_row = table->Insert(sched_row);

  // If the sched row had a negative duration, don't add any descheduled slice
  // because it would be meaningless.
  if (sched_row.dur == -1) {
    return;
  }

  // This will be flushed to the table on the next sched slice (or the very end
  // of the big loop).
  info->desched_ts = ts + dur;
  info->desched_end_state =
      sched.GetTypedColumnByName<StringId>("end_state")[sched_idx];
  info->scheduled_row = id_and_row.row;
}

void ThreadStateGenerator::AddWakingEvent(
    const Table& waking,
    uint32_t waking_idx,
    std::unordered_map<UniqueTid, ThreadSchedInfo>& state_map) {
  int64_t ts = waking.GetTypedColumnByName<int64_t>("ts")[waking_idx];
  UniqueTid utid = static_cast<UniqueTid>(
      waking.GetTypedColumnByName<int64_t>("ref")[waking_idx]);
  ThreadSchedInfo* info = &state_map[utid];

  // Occasionally, it is possible to get a waking event for a thread
  // which is already in a runnable state. When this happens, we just
  // ignore the waking event.
  // See b/186509316 for details and an example on when this happens.
  if (info->desched_end_state &&
      *info->desched_end_state == runnable_string_id_) {
    return;
  }

  // As counter-intuitive as it seems, occasionally we can get a waking
  // event for a thread which is currently running.
  //
  // There are two cases when this can happen:
  // 1. The kernel legitimately send a waking event for a "running" thread
  //    because the thread was woken up before the kernel switched away
  //    from it. In this case, the waking timestamp will be in the past
  //    because we added the descheduled slice when we processed the sched
  //    event.
  // 2. We're close to the end of the trace or had data-loss and we missed
  //    the switch out event for a thread but we see a waking after.

  // Case 1 described above. In this situation, we should drop the waking
  // entirely.
  if (info->desched_ts && *info->desched_ts > ts) {
    return;
  }

  // For case 2 and otherwise, we should just note the fact that the thread
  // became runnable at this time. Note that we cannot check if runnable is
  // already not set because we could have data-loss which leads to us getting
  // back to back waking for a single thread.
  info->runnable_ts = ts;
}

Table::Schema ThreadStateGenerator::CreateSchema() {
  auto schema = tables::ThreadStateTable::Schema();

  // Because we expect our users to generally want ordered by ts, we set the
  // ordering for the schema to match our forced sort pass in ComputeTable.
  auto ts_it = std::find_if(
      schema.columns.begin(), schema.columns.end(),
      [](const Table::Schema::Column& col) { return col.name == "ts"; });
  ts_it->is_sorted = true;
  auto id_it = std::find_if(
      schema.columns.begin(), schema.columns.end(),
      [](const Table::Schema::Column& col) { return col.name == "id"; });
  id_it->is_sorted = false;

  return schema;
}

void ThreadStateGenerator::FlushPendingEventsForThread(
    UniqueTid utid,
    const ThreadSchedInfo& info,
    tables::ThreadStateTable* table,
    base::Optional<int64_t> end_ts) {
  // First, let's flush the descheduled period (if any) to the table.
  if (info.desched_ts) {
    PERFETTO_DCHECK(info.desched_end_state);

    int64_t dur;
    if (end_ts) {
      int64_t desched_end_ts = info.runnable_ts ? *info.runnable_ts : *end_ts;
      dur = desched_end_ts - *info.desched_ts;
    } else {
      dur = -1;
    }

    tables::ThreadStateTable::Row row;
    row.ts = *info.desched_ts;
    row.dur = dur;
    row.state = *info.desched_end_state;
    row.utid = utid;
    row.io_wait = info.io_wait;
    row.blocked_function = info.blocked_function;
    table->Insert(row);
  }

  // Next, flush the runnable period (if any) to the table.
  if (info.runnable_ts) {
    tables::ThreadStateTable::Row row;
    row.ts = *info.runnable_ts;
    row.dur = end_ts ? *end_ts - row.ts : -1;
    row.state = runnable_string_id_;
    row.utid = utid;
    table->Insert(row);
  }
}

void ThreadStateGenerator::AddBlockedReasonEvent(
    const Table& blocked_reason,
    uint32_t blocked_idx,
    std::unordered_map<UniqueTid, ThreadSchedInfo>& state_map) {
  const auto& utid_col = blocked_reason.GetTypedColumnByName<int64_t>("ref");
  const auto& arg_set_id_col =
      blocked_reason.GetTypedColumnByName<uint32_t>("arg_set_id");

  UniqueTid utid = static_cast<UniqueTid>(utid_col[blocked_idx]);
  uint32_t arg_set_id = arg_set_id_col[blocked_idx];
  ThreadSchedInfo& info = state_map[utid];

  base::Optional<Variadic> opt_value;
  util::Status status =
      context_->storage->ExtractArg(arg_set_id, "io_wait", &opt_value);

  // We can't do anything better than ignoring any errors here.
  // TODO(lalitm): see if there's a better way to handle this.
  if (status.ok() && opt_value) {
    PERFETTO_CHECK(opt_value->type == Variadic::Type::kBool);
    info.io_wait = opt_value->bool_value;
  }

  status = context_->storage->ExtractArg(arg_set_id, "function", &opt_value);
  if (status.ok() && opt_value) {
    PERFETTO_CHECK(opt_value->type == Variadic::Type::kString);
    info.blocked_function = opt_value->string_value;
  }
}

std::string ThreadStateGenerator::TableName() {
  return "thread_state";
}

uint32_t ThreadStateGenerator::EstimateRowCount() {
  return context_->storage->sched_slice_table().row_count();
}

}  // namespace trace_processor
}  // namespace perfetto
<commit_msg>tp: fix compile am: 82b99102e5 am: 5203a5a811 am: fd975182a2<commit_after>/*
 * Copyright (C) 2020 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.
 */

#include "src/trace_processor/dynamic/thread_state_generator.h"

#include <memory>
#include <set>

#include "src/trace_processor/types/trace_processor_context.h"

namespace perfetto {
namespace trace_processor {

ThreadStateGenerator::ThreadStateGenerator(TraceProcessorContext* context)
    : running_string_id_(context->storage->InternString("Running")),
      runnable_string_id_(context->storage->InternString("R")),
      context_(context) {}

ThreadStateGenerator::~ThreadStateGenerator() = default;

util::Status ThreadStateGenerator::ValidateConstraints(
    const QueryConstraints&) {
  return util::OkStatus();
}

std::unique_ptr<Table> ThreadStateGenerator::ComputeTable(
    const std::vector<Constraint>&,
    const std::vector<Order>&) {
  if (!unsorted_thread_state_table_) {
    int64_t trace_end_ts =
        context_->storage->GetTraceTimestampBoundsNs().second;

    unsorted_thread_state_table_ = ComputeThreadStateTable(trace_end_ts);

    // We explicitly sort by ts here as ComputeThreadStateTable does not insert
    // rows in sorted order but we expect our clients to always want to sort
    // on ts. Writing ComputeThreadStateTable to insert in sorted order is
    // more trouble than its worth.
    sorted_thread_state_table_ = unsorted_thread_state_table_->Sort(
        {unsorted_thread_state_table_->ts().ascending()});
  }
  PERFETTO_CHECK(sorted_thread_state_table_);
  return std::unique_ptr<Table>(new Table(sorted_thread_state_table_->Copy()));
}

std::unique_ptr<tables::ThreadStateTable>
ThreadStateGenerator::ComputeThreadStateTable(int64_t trace_end_ts) {
  std::unique_ptr<tables::ThreadStateTable> table(new tables::ThreadStateTable(
      context_->storage->mutable_string_pool(), nullptr));

  const auto& raw_sched = context_->storage->sched_slice_table();
  const auto& instants = context_->storage->instant_table();

  // In both tables, exclude utid == 0 which represents the idle thread.
  Table sched = raw_sched.Filter({raw_sched.utid().ne(0)},
                                 RowMap::OptimizeFor::kLookupSpeed);
  Table waking = instants.Filter(
      {instants.name().eq("sched_waking"), instants.ref().ne(0)},
      RowMap::OptimizeFor::kLookupSpeed);

  // We prefer to use waking if at all possible and fall back to wakeup if not
  // available.
  if (waking.row_count() == 0) {
    waking = instants.Filter(
        {instants.name().eq("sched_wakeup"), instants.ref().ne(0)},
        RowMap::OptimizeFor::kLookupSpeed);
  }

  Table sched_blocked_reason = instants.Filter(
      {instants.name().eq("sched_blocked_reason"), instants.ref().ne(0)},
      RowMap::OptimizeFor::kLookupSpeed);

  const auto& sched_ts_col = sched.GetTypedColumnByName<int64_t>("ts");
  const auto& waking_ts_col = waking.GetTypedColumnByName<int64_t>("ts");
  const auto& blocked_ts_col =
      sched_blocked_reason.GetTypedColumnByName<int64_t>("ts");

  uint32_t sched_idx = 0;
  uint32_t waking_idx = 0;
  uint32_t blocked_idx = 0;
  std::unordered_map<UniqueTid, ThreadSchedInfo> state_map;
  while (sched_idx < sched.row_count() || waking_idx < waking.row_count() ||
         blocked_idx < sched_blocked_reason.row_count()) {
    int64_t sched_ts = sched_idx < sched.row_count()
                           ? sched_ts_col[sched_idx]
                           : std::numeric_limits<int64_t>::max();
    int64_t waking_ts = waking_idx < waking.row_count()
                            ? waking_ts_col[waking_idx]
                            : std::numeric_limits<int64_t>::max();
    int64_t blocked_ts = blocked_idx < sched_blocked_reason.row_count()
                             ? blocked_ts_col[blocked_idx]
                             : std::numeric_limits<int64_t>::max();

    // We go through all tables, picking the earliest timestamp from any
    // to process that event.
    int64_t min_ts = std::min({sched_ts, waking_ts, blocked_ts});
    if (min_ts == sched_ts) {
      AddSchedEvent(sched, sched_idx++, state_map, trace_end_ts, table.get());
    } else if (min_ts == waking_ts) {
      AddWakingEvent(waking, waking_idx++, state_map);
    } else /* (min_ts == blocked_ts) */ {
      AddBlockedReasonEvent(sched_blocked_reason, blocked_idx++, state_map);
    }
  }

  // At the end, go through and flush any remaining pending events.
  for (const auto& utid_to_pending_info : state_map) {
    UniqueTid utid = utid_to_pending_info.first;
    const ThreadSchedInfo& pending_info = utid_to_pending_info.second;
    FlushPendingEventsForThread(utid, pending_info, table.get(), base::nullopt);
  }

  return table;
}

void ThreadStateGenerator::AddSchedEvent(
    const Table& sched,
    uint32_t sched_idx,
    std::unordered_map<UniqueTid, ThreadSchedInfo>& state_map,
    int64_t trace_end_ts,
    tables::ThreadStateTable* table) {
  int64_t ts = sched.GetTypedColumnByName<int64_t>("ts")[sched_idx];
  UniqueTid utid = sched.GetTypedColumnByName<uint32_t>("utid")[sched_idx];
  ThreadSchedInfo* info = &state_map[utid];

  // Due to races in the kernel, it is possible for the same thread to be
  // scheduled on different CPUs at the same time. This will manifest itself
  // here by having |info->desched_ts| in the future of this scheduling slice
  // (i.e. there was a scheduling slice in the past which ended after the start
  // of the current scheduling slice).
  //
  // We work around this problem by truncating the previous slice to the start
  // of this slice and not adding the descheduled slice (i.e. we don't call
  // |FlushPendingEventsForThread| which adds this slice).
  //
  // See b/186509316 for details and an example on when this happens.
  if (info->desched_ts && info->desched_ts.value() > ts) {
    uint32_t prev_sched_row = info->scheduled_row.value();
    int64_t prev_sched_start = table->ts()[prev_sched_row];

    // Just a double check that descheduling slice would have started at the
    // same time the scheduling slice would have ended.
    PERFETTO_DCHECK(prev_sched_start + table->dur()[prev_sched_row] ==
                    info->desched_ts.value());

    // Truncate the duration of the old slice to end at the start of this
    // scheduling slice.
    table->mutable_dur()->Set(prev_sched_row, ts - prev_sched_start);
  } else {
    FlushPendingEventsForThread(utid, *info, table, ts);
  }

  // Reset so we don't have any leftover data on the next round.
  *info = {};

  // Undo the expansion of the final sched slice for each CPU to the end of the
  // trace by setting the duration back to -1. This counteracts the code in
  // SchedEventTracker::FlushPendingEvents
  // TODO(lalitm): remove this hack when we stop expanding the last slice to the
  // end of the trace.
  int64_t dur = sched.GetTypedColumnByName<int64_t>("dur")[sched_idx];
  if (ts + dur == trace_end_ts) {
    dur = -1;
  }

  // Now add the sched slice itself as "Running" with the other fields
  // unchanged.
  tables::ThreadStateTable::Row sched_row;
  sched_row.ts = ts;
  sched_row.dur = dur;
  sched_row.cpu = sched.GetTypedColumnByName<uint32_t>("cpu")[sched_idx];
  sched_row.state = running_string_id_;
  sched_row.utid = utid;

  auto id_and_row = table->Insert(sched_row);

  // If the sched row had a negative duration, don't add any descheduled slice
  // because it would be meaningless.
  if (sched_row.dur == -1) {
    return;
  }

  // This will be flushed to the table on the next sched slice (or the very end
  // of the big loop).
  info->desched_ts = ts + dur;
  info->desched_end_state =
      sched.GetTypedColumnByName<StringId>("end_state")[sched_idx];
  info->scheduled_row = id_and_row.row;
}

void ThreadStateGenerator::AddWakingEvent(
    const Table& waking,
    uint32_t waking_idx,
    std::unordered_map<UniqueTid, ThreadSchedInfo>& state_map) {
  int64_t ts = waking.GetTypedColumnByName<int64_t>("ts")[waking_idx];
  UniqueTid utid = static_cast<UniqueTid>(
      waking.GetTypedColumnByName<int64_t>("ref")[waking_idx]);
  ThreadSchedInfo* info = &state_map[utid];

  // Occasionally, it is possible to get a waking event for a thread
  // which is already in a runnable state. When this happens, we just
  // ignore the waking event.
  // See b/186509316 for details and an example on when this happens.
  if (info->desched_end_state &&
      *info->desched_end_state == runnable_string_id_) {
    return;
  }

  // As counter-intuitive as it seems, occasionally we can get a waking
  // event for a thread which is currently running.
  //
  // There are two cases when this can happen:
  // 1. The kernel legitimately send a waking event for a "running" thread
  //    because the thread was woken up before the kernel switched away
  //    from it. In this case, the waking timestamp will be in the past
  //    because we added the descheduled slice when we processed the sched
  //    event.
  // 2. We're close to the end of the trace or had data-loss and we missed
  //    the switch out event for a thread but we see a waking after.

  // Case 1 described above. In this situation, we should drop the waking
  // entirely.
  if (info->desched_ts && *info->desched_ts > ts) {
    return;
  }

  // For case 2 and otherwise, we should just note the fact that the thread
  // became runnable at this time. Note that we cannot check if runnable is
  // already not set because we could have data-loss which leads to us getting
  // back to back waking for a single thread.
  info->runnable_ts = ts;
}

Table::Schema ThreadStateGenerator::CreateSchema() {
  auto schema = tables::ThreadStateTable::Schema();

  // Because we expect our users to generally want ordered by ts, we set the
  // ordering for the schema to match our forced sort pass in ComputeTable.
  auto ts_it = std::find_if(
      schema.columns.begin(), schema.columns.end(),
      [](const Table::Schema::Column& col) { return col.name == "ts"; });
  ts_it->is_sorted = true;
  auto id_it = std::find_if(
      schema.columns.begin(), schema.columns.end(),
      [](const Table::Schema::Column& col) { return col.name == "id"; });
  id_it->is_sorted = false;

  return schema;
}

void ThreadStateGenerator::FlushPendingEventsForThread(
    UniqueTid utid,
    const ThreadSchedInfo& info,
    tables::ThreadStateTable* table,
    base::Optional<int64_t> end_ts) {
  // First, let's flush the descheduled period (if any) to the table.
  if (info.desched_ts) {
    PERFETTO_DCHECK(info.desched_end_state);

    int64_t dur;
    if (end_ts) {
      int64_t desched_end_ts = info.runnable_ts ? *info.runnable_ts : *end_ts;
      dur = desched_end_ts - *info.desched_ts;
    } else {
      dur = -1;
    }

    tables::ThreadStateTable::Row row;
    row.ts = *info.desched_ts;
    row.dur = dur;
    row.state = *info.desched_end_state;
    row.utid = utid;
    row.io_wait = info.io_wait;
    row.blocked_function = info.blocked_function;
    table->Insert(row);
  }

  // Next, flush the runnable period (if any) to the table.
  if (info.runnable_ts) {
    tables::ThreadStateTable::Row row;
    row.ts = *info.runnable_ts;
    row.dur = end_ts ? *end_ts - row.ts : -1;
    row.state = runnable_string_id_;
    row.utid = utid;
    table->Insert(row);
  }
}

void ThreadStateGenerator::AddBlockedReasonEvent(
    const Table& blocked_reason,
    uint32_t blocked_idx,
    std::unordered_map<UniqueTid, ThreadSchedInfo>& state_map) {
  const auto& utid_col = blocked_reason.GetTypedColumnByName<int64_t>("ref");
  const auto& arg_set_id_col =
      blocked_reason.GetTypedColumnByName<uint32_t>("arg_set_id");

  UniqueTid utid = static_cast<UniqueTid>(utid_col[blocked_idx]);
  uint32_t arg_set_id = arg_set_id_col[blocked_idx];
  ThreadSchedInfo& info = state_map[utid];

  base::Optional<Variadic> opt_value;
  util::Status status =
      context_->storage->ExtractArg(arg_set_id, "io_wait", &opt_value);

  // We can't do anything better than ignoring any errors here.
  // TODO(lalitm): see if there's a better way to handle this.
  if (status.ok() && opt_value) {
    PERFETTO_CHECK(opt_value->type == Variadic::Type::kBool);
    info.io_wait = opt_value->bool_value;
  }

  status = context_->storage->ExtractArg(arg_set_id, "function", &opt_value);
  if (status.ok() && opt_value) {
    PERFETTO_CHECK(opt_value->type == Variadic::Type::kString);
    info.blocked_function = opt_value->string_value;
  }
}

std::string ThreadStateGenerator::TableName() {
  return "thread_state";
}

uint32_t ThreadStateGenerator::EstimateRowCount() {
  return context_->storage->sched_slice_table().row_count();
}

}  // namespace trace_processor
}  // namespace perfetto
<|endoftext|>