text: // Copyright 2012 Google Inc. All Rights Reserved.
  //
  // 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 "syzygy/simulate/heat_map_simulation.h"
  
  #include <functional>
  
  namespace simulate {
  
  HeatMapSimulation::HeatMapSimulation()
      : time_slice_usecs_(kDefaultTimeSliceSize),
        memory_slice_bytes_(kDefaultMemorySliceSize),
        max_time_slice_usecs_(0),
        max_memory_slice_bytes_(0),
        output_individual_functions_(false) {
  }
  
  bool HeatMapSimulation::TimeSlice::PrintJSONFunctions(
      core::JSONFileWriter& json_file,
      const HeatMapSimulation::TimeSlice::FunctionMap& functions) {
    typedef std::pair<uint32_t, base::StringPiece> QtyNamePair;
    std::vector<QtyNamePair> ordered_functions(functions.size());
  
    FunctionMap::const_iterator functions_iter = functions.begin();
    uint32_t i = 0;
    for (; functions_iter != functions.end(); ++functions_iter, ++i) {
      ordered_functions[i] = QtyNamePair(functions_iter->second,
                                         functions_iter->first);
    }
    std::sort(ordered_functions.begin(),
              ordered_functions.end(),
              std::greater<QtyNamePair>());
  
    if (!json_file.OutputKey("functions") ||
        !json_file.OpenList())
      return false;
  
    for (uint32_t i = 0; i < ordered_functions.size(); ++i) {
      if (!json_file.OpenDict() ||
          !json_file.OutputKey("name") ||
          !json_file.OutputString(ordered_functions[i].second.data()) ||
          !json_file.OutputKey("quantity") ||
          !json_file.OutputInteger(ordered_functions[i].first) ||
          !json_file.CloseDict())
        return false;
    }
  
    if (!json_file.CloseList())
      return false;
  
    return true;
  }
  
  bool HeatMapSimulation::SerializeToJSON(FILE* output, bool pretty_print) {
    typedef TimeSlice::FunctionMap FunctionMap;
  
    DCHECK(output != NULL);
  
    core::JSONFileWriter json_file(output, pretty_print);
  
    if (!json_file.OpenDict() ||
        !json_file.OutputKey("time_slice_usecs") ||
        !json_file.OutputInteger(time_slice_usecs_) ||
        !json_file.OutputKey("memory_slice_bytes") ||
        !json_file.OutputInteger(memory_slice_bytes_) ||
        !json_file.OutputKey("max_time_slice_usecs") ||
        !json_file.OutputInteger(max_time_slice_usecs_) ||
        !json_file.OutputKey("max_memory_slice_bytes") ||
        !json_file.OutputInteger(max_memory_slice_bytes_) ||
        !json_file.OutputKey("time_slice_list") ||
        !json_file.OpenList()) {
      return false;
    }
  
    TimeMemoryMap::const_iterator time_memory_iter = time_memory_map_.begin();
    for (; time_memory_iter != time_memory_map_.end(); ++time_memory_iter) {
      time_t time = time_memory_iter->first;
      uint32_t total = time_memory_iter->second.total();
      const TimeSlice& time_slice = time_memory_iter->second;
  
      if (!json_file.OpenDict() ||
          !json_file.OutputKey("timestamp") ||
          !json_file.OutputInteger(time) ||
          !json_file.OutputKey("total_memory_slices") ||
          !json_file.OutputInteger(total) ||
          !json_file.OutputKey("memory_slice_list") ||
          !json_file.OpenList()) {
        return false;
      }
  
      TimeSlice::MemorySliceMap::const_iterator slices_iter =
          time_slice.slices().begin();
  
      for (; slices_iter != time_slice.slices().end(); ++slices_iter) {
        if (!json_file.OpenDict() ||
            !json_file.OutputKey("memory_slice") ||
            !json_file.OutputInteger(slices_iter->first) ||
            !json_file.OutputKey("quantity") ||
            !json_file.OutputInteger(slices_iter->second.total))
          return false;
  
        if (output_individual_functions_) {
          if (!TimeSlice::PrintJSONFunctions(json_file,
                                             slices_iter->second.functions))
            return false;
        }
  
        if (!json_file.CloseDict())
          return false;
      }
  
      if (!json_file.CloseList() ||
          !json_file.CloseDict())
        return false;
    }
  
    if (!json_file.CloseList() ||
        !json_file.CloseDict())
      return false;
  
    return json_file.Finished();
  }
  
  void HeatMapSimulation::OnProcessStarted(base::Time time,
                                           size_t /*default_page_size*/) {
    // Set the entry time of this process.
    process_start_time_ = time;
  }
  
  void HeatMapSimulation::OnFunctionEntry(base::Time time,
                                          const Block* block) {
    // Get the time when this function was called since the process start.
    time_t relative_time = (time - process_start_time_).InMicroseconds();
  
    // Since we will insert to a map many TimeSlices with the same entry time,
    // we can pass RegisterFunction a reference to the TimeSlice in the map.
    // This way, RegisterFunction doesn't have to search for that position
    // every time it gets called and the time complexity gets reduced
    // in a logarithmic scale.
    TimeSliceId time_slice = relative_time / time_slice_usecs_;
    TimeSlice& slice = time_memory_map_[time_slice];
  
    max_time_slice_usecs_ = std::max(max_time_slice_usecs_, time_slice);
  
    DCHECK(block != NULL);
    DCHECK(memory_slice_bytes_ != 0);
    const uint32_t block_start = block->addr().value();
    const uint32_t size = block->size();
    const std::string& name = block->name();
  
    const uint32_t first_slice = block_start / memory_slice_bytes_;
    const uint32_t last_slice = (block_start + size - 1) / memory_slice_bytes_;
    if (first_slice == last_slice) {
      // This function fits in a single memory slice. Add it to our time slice.
      slice.AddSlice(first_slice, name, size);
    } else {
      // This function takes several memory slices. Add the first and last
      // slices to our time slice only with the part of the slice they use,
      // and then loop through the rest and add the whole slices.
      const uint32_t leading_bytes =
          memory_slice_bytes_ - block_start % memory_slice_bytes_;
  
      const uint32_t trailing_bytes =
          ((block_start + size - 1 + memory_slice_bytes_) % memory_slice_bytes_) +
          1;
  
      slice.AddSlice(first_slice, name, leading_bytes);
      slice.AddSlice(last_slice, name, trailing_bytes);
  
      const uint32_t kStartIndex = block_start / memory_slice_bytes_ + 1;
      const uint32_t kEndIndex = (block_start + size - 1) / memory_slice_bytes_;
  
      for (uint32_t i = kStartIndex; i < kEndIndex; i++)
        slice.AddSlice(i, name, memory_slice_bytes_);
    }
  
    max_memory_slice_bytes_ = std::max(max_memory_slice_bytes_, last_slice);
  }
  
  }  // namespace simulate
  