| /* ********************************************************** |
| * Copyright (c) 2015-2023 Google, Inc. All rights reserved. |
| * **********************************************************/ |
| |
| /* |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are met: |
| * |
| * * Redistributions of source code must retain the above copyright notice, |
| * this list of conditions and the following disclaimer. |
| * |
| * * Redistributions in binary form must reproduce the above copyright notice, |
| * this list of conditions and the following disclaimer in the documentation |
| * and/or other materials provided with the distribution. |
| * |
| * * Neither the name of Google, Inc. nor the names of its contributors may be |
| * used to endorse or promote products derived from this software without |
| * specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
| * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| * ARE DISCLAIMED. IN NO EVENT SHALL VMWARE, INC. OR CONTRIBUTORS BE LIABLE |
| * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
| * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
| * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
| * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
| * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH |
| * DAMAGE. |
| */ |
| |
| #include "caching_device_stats.h" |
| |
| #include <assert.h> |
| #include <stdint.h> |
| #ifdef HAS_ZLIB |
| # include <zlib.h> |
| #endif |
| |
| #include <iomanip> |
| #include <iostream> |
| #include <locale> |
| #include <map> |
| #include <string> |
| #include <utility> |
| |
| #include "memref.h" |
| #include "options.h" |
| #include "caching_device_block.h" |
| #include "trace_entry.h" |
| |
| namespace dynamorio { |
| namespace drmemtrace { |
| |
| caching_device_stats_t::caching_device_stats_t(const std::string &miss_file, |
| int block_size, bool warmup_enabled, |
| bool is_coherent) |
| : success_(true) |
| , num_hits_(0) |
| , num_misses_(0) |
| , num_compulsory_misses_(0) |
| , num_child_hits_(0) |
| , num_inclusive_invalidates_(0) |
| , num_coherence_invalidates_(0) |
| , num_exclusive_invalidates_(0) |
| , num_hits_at_reset_(0) |
| , num_misses_at_reset_(0) |
| , num_child_hits_at_reset_(0) |
| , warmup_enabled_(warmup_enabled) |
| , is_coherent_(is_coherent) |
| , access_count_(block_size) |
| , file_(nullptr) |
| , caching_device_(nullptr) |
| { |
| if (miss_file.empty()) { |
| dump_misses_ = false; |
| } else { |
| #ifdef HAS_ZLIB |
| file_ = gzopen(miss_file.c_str(), "w"); |
| #else |
| file_ = fopen(miss_file.c_str(), "w"); |
| #endif |
| if (file_ == nullptr) { |
| dump_misses_ = false; |
| success_ = false; |
| } else |
| dump_misses_ = true; |
| } |
| |
| stats_map_.emplace(metric_name_t::HITS, num_hits_); |
| stats_map_.emplace(metric_name_t::MISSES, num_misses_); |
| stats_map_.emplace(metric_name_t::HITS_AT_RESET, num_hits_at_reset_); |
| stats_map_.emplace(metric_name_t::MISSES_AT_RESET, num_misses_at_reset_); |
| stats_map_.emplace(metric_name_t::COMPULSORY_MISSES, num_compulsory_misses_); |
| stats_map_.emplace(metric_name_t::CHILD_HITS_AT_RESET, num_child_hits_at_reset_); |
| stats_map_.emplace(metric_name_t::CHILD_HITS, num_child_hits_); |
| stats_map_.emplace(metric_name_t::INCLUSIVE_INVALIDATES, num_inclusive_invalidates_); |
| stats_map_.emplace(metric_name_t::COHERENCE_INVALIDATES, num_coherence_invalidates_); |
| stats_map_.emplace(metric_name_t::EXCLUSIVE_INVALIDATES, num_exclusive_invalidates_); |
| } |
| |
| caching_device_stats_t::~caching_device_stats_t() |
| { |
| if (file_ != nullptr) { |
| #ifdef HAS_ZLIB |
| gzclose(file_); |
| #else |
| fclose(file_); |
| #endif |
| } |
| } |
| |
| void |
| caching_device_stats_t::access(const memref_t &memref, bool hit, |
| caching_device_block_t *cache_block) |
| { |
| // We assume we're single-threaded. |
| // We're only computing miss rate so we just inc counters here. |
| if (hit) |
| num_hits_++; |
| else { |
| num_misses_++; |
| if (dump_misses_) |
| dump_miss(memref); |
| |
| check_compulsory_miss(memref.data.addr); |
| } |
| } |
| |
| void |
| caching_device_stats_t::child_access(const memref_t &memref, bool hit, |
| caching_device_block_t *cache_block) |
| { |
| if (hit) |
| num_child_hits_++; |
| // else being computed in access() |
| } |
| |
| void |
| caching_device_stats_t::check_compulsory_miss(addr_t addr) |
| { |
| auto lookup_pair = access_count_.lookup(addr); |
| |
| // If the address has never been accessed insert proper bound into access_count_ |
| // and count it as a compulsory miss. |
| if (!lookup_pair.first) { |
| num_compulsory_misses_++; |
| access_count_.insert(addr, lookup_pair.second); |
| } |
| } |
| |
| void |
| caching_device_stats_t::dump_miss(const memref_t &memref) |
| { |
| addr_t pc, addr; |
| memref_pid_t pid; |
| if (type_is_instr(memref.instr.type)) |
| pc = memref.instr.addr; |
| else { // data ref: others shouldn't get here |
| assert(type_is_prefetch(memref.data.type) || |
| memref.data.type == TRACE_TYPE_READ || |
| memref.data.type == TRACE_TYPE_WRITE); |
| pc = memref.data.pc; |
| } |
| addr = memref.data.addr; |
| pid = memref.data.pid; |
| |
| // XXX: This writing method to the same file from multiple processes is racy. |
| // It works most of the time but consider using a directory with individual files |
| // per process as a future safer alternative. |
| #ifdef HAS_ZLIB |
| gzprintf(file_, "%lld,0x%zx,0x%zx\n", pid, pc, addr); |
| #else |
| fprintf(file_, "%lld,0x%zx,0x%zx\n", pid, pc, addr); |
| #endif |
| } |
| |
| void |
| caching_device_stats_t::print_warmup(std::string prefix) |
| { |
| std::cerr << prefix << std::setw(18) << std::left << "Warmup hits:" << std::setw(20) |
| << std::right << num_hits_at_reset_ << std::endl; |
| std::cerr << prefix << std::setw(18) << std::left << "Warmup misses:" << std::setw(20) |
| << std::right << num_misses_at_reset_ << std::endl; |
| } |
| |
| void |
| caching_device_stats_t::print_counts(std::string prefix) |
| { |
| std::cerr << prefix << std::setw(18) << std::left << "Hits:" << std::setw(20) |
| << std::right << num_hits_ << std::endl; |
| std::cerr << prefix << std::setw(18) << std::left << "Misses:" << std::setw(20) |
| << std::right << num_misses_ << std::endl; |
| std::cerr << prefix << std::setw(18) << std::left |
| << "Compulsory misses:" << std::setw(20) << std::right |
| << num_compulsory_misses_ << std::endl; |
| if (is_coherent_) { |
| std::cerr << prefix << std::setw(21) << std::left |
| << "Parent invalidations:" << std::setw(17) << std::right |
| << num_inclusive_invalidates_ + num_exclusive_invalidates_ << std::endl; |
| std::cerr << prefix << std::setw(20) << std::left |
| << "Write invalidations:" << std::setw(18) << std::right |
| << num_coherence_invalidates_ << std::endl; |
| } else { |
| std::cerr << prefix << std::setw(18) << std::left |
| << "Invalidations:" << std::setw(20) << std::right |
| << num_inclusive_invalidates_ + num_exclusive_invalidates_ << std::endl; |
| } |
| } |
| |
| void |
| caching_device_stats_t::print_rates(std::string prefix) |
| { |
| if (num_hits_ + num_misses_ > 0) { |
| std::string miss_label = "Miss rate:"; |
| if (num_child_hits_ != 0) |
| miss_label = "Local miss rate:"; |
| std::cerr << prefix << std::setw(18) << std::left << miss_label << std::setw(20) |
| << std::fixed << std::setprecision(2) << std::right |
| << ((float)num_misses_ * 100 / (num_hits_ + num_misses_)) << "%" |
| << std::endl; |
| } |
| } |
| |
| void |
| caching_device_stats_t::print_child_stats(std::string prefix) |
| { |
| if (num_child_hits_ != 0) { |
| std::cerr << prefix << std::setw(18) << std::left |
| << "Child hits:" << std::setw(20) << std::right << num_child_hits_ |
| << std::endl; |
| std::cerr << prefix << std::setw(18) << std::left |
| << "Total miss rate:" << std::setw(20) << std::fixed |
| << std::setprecision(2) << std::right |
| << ((float)num_misses_ * 100 / |
| (num_hits_ + num_child_hits_ + num_misses_)) |
| << "%" << std::endl; |
| } |
| } |
| |
| void |
| caching_device_stats_t::print_stats(std::string prefix) |
| { |
| std::cerr.imbue(std::locale("")); // Add commas, at least for my locale |
| if (warmup_enabled_) { |
| print_warmup(prefix); |
| } |
| print_counts(prefix); |
| print_rates(prefix); |
| print_child_stats(prefix); |
| std::cerr.imbue(std::locale("C")); // Reset to avoid affecting later prints. |
| } |
| |
| void |
| caching_device_stats_t::reset() |
| { |
| num_hits_at_reset_ = num_hits_; |
| num_misses_at_reset_ = num_misses_; |
| num_child_hits_at_reset_ = num_child_hits_; |
| num_hits_ = 0; |
| num_misses_ = 0; |
| num_compulsory_misses_ = 0; |
| num_child_hits_ = 0; |
| num_inclusive_invalidates_ = 0; |
| num_coherence_invalidates_ = 0; |
| num_exclusive_invalidates_ = 0; |
| } |
| |
| void |
| caching_device_stats_t::invalidate(invalidation_type_t invalidation_type) |
| { |
| if (invalidation_type == INVALIDATION_INCLUSIVE) { |
| num_inclusive_invalidates_++; |
| } else if (invalidation_type == INVALIDATION_COHERENCE) { |
| num_coherence_invalidates_++; |
| } else if (invalidation_type == INVALIDATION_EXCLUSIVE) { |
| num_exclusive_invalidates_++; |
| } |
| } |
| |
| } // namespace drmemtrace |
| } // namespace dynamorio |