Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  dexanalyze_experiments.cc

  Sprache: C
 

/*
 * Copyright (C) 2018 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 "dexanalyze_experiments.h"

#include <algorithm>
#include <stdint.h>
#include <inttypes.h>
#include <iostream>
#include <map>
#include <vector>

#include "android-base/stringprintf.h"
#include "dex/class_accessor-inl.h"
#include "dex/class_iterator.h"
#include "dex/code_item_accessors-inl.h"
#include "dex/dex_instruction-inl.h"
#include "dex/standard_dex_file.h"
#include "dex/utf-inl.h"

namespace art {
namespace dexanalyze {

bool IsRange(Instruction::Code code) {
  return code == Instruction::INVOKE_VIRTUAL_RANGE ||
      code == Instruction::INVOKE_DIRECT_RANGE ||
      code == Instruction::INVOKE_SUPER_RANGE ||
      code == Instruction::INVOKE_STATIC_RANGE ||
      code == Instruction::INVOKE_INTERFACE_RANGE;
}

uint16_t NumberOfArgs(const Instruction& inst) {
  return IsRange(inst.Opcode()) ? inst.VRegA_3rc() : inst.VRegA_35c();
}

uint16_t DexMethodIndex(const Instruction& inst) {
  return IsRange(inst.Opcode()) ? inst.VRegB_3rc() : inst.VRegB_35c();
}

std::string Percent(uint64_t value, uint64_t max) {
  if (max == 0) {
    return "0";
  }
  return android::base::StringPrintf(
      "%" PRId64 "(%.2f%%)",
      value,
      static_cast<double>(value * 100) / static_cast<double>(max));
}

std::string PercentDivide(uint64_t value, uint64_t max) {
  if (max == 0) {
    return "0";
  }
  return android::base::StringPrintf(
      "%" PRId64 "/%" PRId64 "(%.2f%%)",
      value,
      max,
      static_cast<double>(value * 100) / static_cast<double>(max));
}

size_t PrefixLen(const std::string& a, const std::string& b) {
  size_t len = 0;
  for (; len < a.length() && len < b.length() && a[len] == b[len]; ++len) {}
  return len;
}

void Experiment::ProcessDexFiles(const std::vector<std::unique_ptr<const DexFile>>& dex_files) {
  for (const std::unique_ptr<const DexFile>& dex_file : dex_files) {
    ProcessDexFile(*dex_file);
  }
}

void AnalyzeDebugInfo::ProcessDexFiles(
    const std::vector<std::unique_ptr<const DexFile>>& dex_files) {
  std::set<const uint8_t*> seen;
  std::vector<size_t> counts(2560u);
  std::vector<size_t> opcode_counts(2560u);
  std::set<std::vector<uint8_t>> unique_non_header;
  for (const std::unique_ptr<const DexFile>& dex_file : dex_files) {
    for (ClassAccessor accessor : dex_file->GetClasses()) {
      for (const ClassAccessor::Method& method : accessor.GetMethods()) {
        CodeItemDebugInfoAccessor code_item(*dex_file, method.GetCodeItem(), method.GetIndex());
        const uint8_t* debug_info = dex_file->GetDebugInfoStream(code_item.DebugInfoOffset());
        if (debug_info != nullptr && seen.insert(debug_info).second) {
          const uint8_t* stream = debug_info;
          DecodeUnsignedLeb128(&stream);  // line_start
          uint32_t parameters_size = DecodeUnsignedLeb128(&stream);
          for (uint32_t i = 0; i < parameters_size; ++i) {
            DecodeUnsignedLeb128P1(&stream);  // Parameter name.
          }
          bool done = false;
          const uint8_t* after_header_start = stream;
          while (!done) {
            const uint8_t* const op_start = stream;
            uint8_t opcode = *stream++;
            ++opcode_counts[opcode];
            ++total_opcode_bytes_;
            switch (opcode) {
              case DexFile::DBG_END_SEQUENCE:
                ++total_end_seq_bytes_;
                done = true;
                break;
              case DexFile::DBG_ADVANCE_PC:
                DecodeUnsignedLeb128(&stream);  // addr_diff
                total_advance_pc_bytes_ += stream - op_start;
                break;
              case DexFile::DBG_ADVANCE_LINE:
                DecodeSignedLeb128(&stream);  // line_diff
                total_advance_line_bytes_ += stream - op_start;
                break;
              case DexFile::DBG_START_LOCAL:
                DecodeUnsignedLeb128(&stream);  // register_num
                DecodeUnsignedLeb128P1(&stream);  // name_idx
                DecodeUnsignedLeb128P1(&stream);  // type_idx
                total_start_local_bytes_ += stream - op_start;
                break;
              case DexFile::DBG_START_LOCAL_EXTENDED:
                DecodeUnsignedLeb128(&stream);  // register_num
                DecodeUnsignedLeb128P1(&stream);  // name_idx
                DecodeUnsignedLeb128P1(&stream);  // type_idx
                DecodeUnsignedLeb128P1(&stream);  // sig_idx
                total_start_local_extended_bytes_ += stream - op_start;
                break;
              case DexFile::DBG_END_LOCAL:
                DecodeUnsignedLeb128(&stream);  // register_num
                total_end_local_bytes_ += stream - op_start;
                break;
              case DexFile::DBG_RESTART_LOCAL:
                DecodeUnsignedLeb128(&stream);  // register_num
                total_restart_local_bytes_ += stream - op_start;
                break;
              case DexFile::DBG_SET_PROLOGUE_END:
              case DexFile::DBG_SET_EPILOGUE_BEGIN:
                total_epilogue_bytes_ += stream - op_start;
                break;
              case DexFile::DBG_SET_FILE: {
                DecodeUnsignedLeb128P1(&stream);  // name_idx
                total_set_file_bytes_ += stream - op_start;
                break;
              }
              default: {
                total_other_bytes_ += stream - op_start;
                break;
              }
            }
          }
          const size_t bytes = stream - debug_info;
          total_bytes_ += bytes;
          total_non_header_bytes_ += stream - after_header_start;
          if (unique_non_header.insert(std::vector<uint8_t>(after_header_start, stream)).second) {
            total_unique_non_header_bytes_ += stream - after_header_start;
          }
          for (size_t i = 0; i < bytes; ++i) {
            ++counts[debug_info[i]];
          }
        }
      }
    }
  }
  auto calc_entropy = [](std::vector<size_t> data) {
    size_t total = std::accumulate(data.begin(), data.end(), 0u);
    double avg_entropy = 0.0;
    for (size_t c : data) {
      if (c > 0) {
        double ratio = static_cast<double>(c) / static_cast<double>(total);
        avg_entropy -= ratio * log(ratio) / log(256.0);
      }
    }
    return avg_entropy * total;
  };
  total_entropy_ += calc_entropy(counts);
  total_opcode_entropy_ += calc_entropy(opcode_counts);
}

void AnalyzeDebugInfo::Dump(std::ostream& os, uint64_t total_size) const {
  os << "Debug info bytes " << Percent(total_bytes_, total_size) << "\n";

  os << "  DBG_END_SEQUENCE: " << Percent(total_end_seq_bytes_, total_size) << "\n";
  os << "  DBG_ADVANCE_PC: " << Percent(total_advance_pc_bytes_, total_size) << "\n";
  os << "  DBG_ADVANCE_LINE: " << Percent(total_advance_line_bytes_, total_size) << "\n";
  os << "  DBG_START_LOCAL: " << Percent(total_start_local_bytes_, total_size) << "\n";
  os << "  DBG_START_LOCAL_EXTENDED: "
     << Percent(total_start_local_extended_bytes_, total_size) << "\n";
  os << "  DBG_END_LOCAL: " << Percent(total_end_local_bytes_, total_size) << "\n";
  os << "  DBG_RESTART_LOCAL: " << Percent(total_restart_local_bytes_, total_size) << "\n";
  os << "  DBG_SET_PROLOGUE bytes " << Percent(total_epilogue_bytes_, total_size) << "\n";
  os << "  DBG_SET_FILE bytes " << Percent(total_set_file_bytes_, total_size) << "\n";
  os << "  special: "
      << Percent(total_other_bytes_, total_size) << "\n";
  os << "Debug info entropy " << Percent(total_entropy_, total_size) << "\n";
  os << "Debug info opcode bytes " << Percent(total_opcode_bytes_, total_size) << "\n";
  os << "Debug info opcode entropy " << Percent(total_opcode_entropy_, total_size) << "\n";
  os << "Debug info non header bytes " << Percent(total_non_header_bytes_, total_size) << "\n";
  os << "Debug info deduped non header bytes "
     << Percent(total_unique_non_header_bytes_, total_size) << "\n";
}

void CountDexIndices::ProcessDexFiles(
    const std::vector<std::unique_ptr<const DexFile>>& dex_files) {
  std::set<std::string> unique_field_names;
  std::set<std::string> unique_method_names;
  std::set<std::string> unique_type_names;
  std::set<std::string> unique_mf_names;
  for (const std::unique_ptr<const DexFile>& dex_file : dex_files) {
    for (size_t i = 0; i < dex_file->NumTypeIds(); ++i) {
      unique_type_names.insert(
          dex_file->GetStringData(dex_file->GetTypeId(dex::TypeIndex(i)).descriptor_idx_));
    }
    for (size_t i = 0; i < dex_file->NumFieldIds(); ++i) {
      unique_field_names.insert(dex_file->GetStringData(dex_file->GetFieldId(i).name_idx_));
    }
    for (size_t i = 0; i < dex_file->NumMethodIds(); ++i) {
      unique_method_names.insert(dex_file->GetStringData(dex_file->GetMethodId(i).name_idx_));
    }
    ProcessDexFile(*dex_file);
  }
  total_unique_method_names_ += unique_method_names.size();
  total_unique_field_names_ += unique_field_names.size();
  total_unique_type_names_ += unique_type_names.size();
  unique_mf_names = unique_field_names;
  unique_mf_names.insert(unique_method_names.begin(), unique_method_names.end());
  total_unique_mf_names_ += unique_mf_names.size();
}

void CountDexIndices::ProcessDexFile(const DexFile& dex_file) {
  num_string_ids_ += dex_file.NumStringIds();
  num_method_ids_ += dex_file.NumMethodIds();
  num_field_ids_ += dex_file.NumFieldIds();
  num_type_ids_ += dex_file.NumTypeIds();
  num_class_defs_ += dex_file.NumClassDefs();
  std::set<size_t> unique_code_items;

  for (ClassAccessor accessor : dex_file.GetClasses()) {
    std::set<size_t> unique_method_ids;
    std::set<size_t> unique_string_ids;
    // Types accessed and count.
    std::map<size_t, size_t> types_accessed;

    // Maps from dex field index -> class field index (static or instance).
    std::map<uint32_t, uint32_t> static_field_index_map_;
    size_t current_idx = 0u;
    for (const ClassAccessor::Field& field : accessor.GetStaticFields()) {
      static_field_index_map_[field.GetIndex()] = current_idx++;
    }
    std::map<uint32_t, uint32_t> instance_field_index_map_;
    current_idx = 0u;
    for (const ClassAccessor::Field& field : accessor.GetInstanceFields()) {
      instance_field_index_map_[field.GetIndex()] = current_idx++;
    }
    auto ProcessFieldIndex = [&](uint32_t dex_field_idx,
                                 uint32_t inout,
                                 const std::map<uint32_t, uint32_t>& index_map,
                                 /*inout*/ FieldAccessStats* stats) {
      auto it = index_map.find(dex_field_idx);
      if (it != index_map.end()) {
        if (it->second < FieldAccessStats::kMaxFieldIndex) {
          ++stats->field_index_[it->second];
        } else {
          ++stats->field_index_other_;
        }
      } else {
        ++stats->field_index_other_class_;
      }
      if (it != index_map.end() &&
          it->second < FieldAccessStats::kShortBytecodeFieldIndexOutCutOff &&
          inout < FieldAccessStats::kShortBytecodeInOutCutOff) {
        ++stats->short_bytecode_;
      }
    };
    auto ProcessInstanceField = [&](const Instruction& inst,
                                    uint32_t first_arg_reg,
                                    const std::map<uint32_t, uint32_t>& index_map,
                                    /*inout*/ InstanceFieldAccessStats* stats) {
      const uint32_t dex_field_idx = inst.VRegC_22c();
      ++types_accessed[dex_file.GetFieldId(dex_field_idx).class_idx_.index_];
      uint32_t input = inst.VRegA_22c();
      ++stats->inout_[input];
      const uint32_t receiver = inst.VRegB_22c();
      // FIXME: This is weird if receiver < first_arg_reg.
      ++stats->receiver_[(receiver - first_arg_reg) & 0xF];
      if (first_arg_reg == receiver) {
        ProcessFieldIndex(dex_field_idx, input, index_map, stats);
      }
    };
    auto ProcessStaticField = [&](const Instruction& inst,
                                  const std::map<uint32_t, uint32_t>& index_map,
                                  /*inout*/ StaticFieldAccessStats* stats) {
      const uint32_t dex_field_idx = inst.VRegB_21c();
      ++types_accessed[dex_file.GetFieldId(dex_field_idx).class_idx_.index_];
      uint8_t output = inst.VRegA_21c();
      if (output < 16u) {
        ++stats->inout_[output];
      } else {
        ++stats->inout_other_;
      }
      ProcessFieldIndex(dex_field_idx, output, index_map, stats);
    };

    for (const ClassAccessor::Method& method : accessor.GetMethods()) {
      CodeItemDataAccessor code_item(dex_file, method.GetCodeItem());
      const uint32_t first_arg_reg =
          ((method.GetAccessFlags() & kAccStatic) == 0)
          ? code_item.RegistersSize() - code_item.InsSize()
          : static_cast<uint32_t>(-1);

      dex_code_bytes_ += code_item.InsnsSizeInBytes();
      unique_code_items.insert(method.GetCodeItemOffset());
      for (const DexInstructionPcPair& inst : code_item) {
        switch (inst->Opcode()) {
          case Instruction::CONST_STRING: {
            const dex::StringIndex string_index(inst->VRegB_21c());
            unique_string_ids.insert(string_index.index_);
            ++num_string_ids_from_code_;
            break;
          }
          case Instruction::IGET:
          case Instruction::IGET_WIDE:
          case Instruction::IGET_OBJECT:
          case Instruction::IGET_BOOLEAN:
          case Instruction::IGET_BYTE:
          case Instruction::IGET_CHAR:
          case Instruction::IGET_SHORT: {
            ProcessInstanceField(
                inst.Inst(), first_arg_reg, instance_field_index_map_, &iget_stats_);
            break;
          }
          case Instruction::IPUT:
          case Instruction::IPUT_WIDE:
          case Instruction::IPUT_OBJECT:
          case Instruction::IPUT_BOOLEAN:
          case Instruction::IPUT_BYTE:
          case Instruction::IPUT_CHAR:
          case Instruction::IPUT_SHORT: {
            ProcessInstanceField(
                inst.Inst(), first_arg_reg, instance_field_index_map_, &iput_stats_);
            break;
          }
          case Instruction::SGET:
          case Instruction::SGET_WIDE:
          case Instruction::SGET_OBJECT:
          case Instruction::SGET_BOOLEAN:
          case Instruction::SGET_BYTE:
          case Instruction::SGET_CHAR:
          case Instruction::SGET_SHORT: {
            ProcessStaticField(inst.Inst(), static_field_index_map_, &sget_stats_);
            break;
          }
          case Instruction::SPUT:
          case Instruction::SPUT_WIDE:
          case Instruction::SPUT_OBJECT:
          case Instruction::SPUT_BOOLEAN:
          case Instruction::SPUT_BYTE:
          case Instruction::SPUT_CHAR:
          case Instruction::SPUT_SHORT: {
            ProcessStaticField(inst.Inst(), static_field_index_map_, &sput_stats_);
            break;
          }
          case Instruction::CONST_STRING_JUMBO: {
            const dex::StringIndex string_index(inst->VRegB_31c());
            unique_string_ids.insert(string_index.index_);
            ++num_string_ids_from_code_;
            break;
          }
          // Invoke cases.
          case Instruction::INVOKE_VIRTUAL:
          case Instruction::INVOKE_VIRTUAL_RANGE: {
            uint32_t method_idx = DexMethodIndex(inst.Inst());
            ++types_accessed[dex_file.GetMethodId(method_idx).class_idx_.index_];
            if (dex_file.GetMethodId(method_idx).class_idx_ == accessor.GetClassIdx()) {
              ++same_class_virtual_;
            }
            ++total_virtual_;
            unique_method_ids.insert(method_idx);
            break;
          }
          case Instruction::INVOKE_DIRECT:
          case Instruction::INVOKE_DIRECT_RANGE: {
            uint32_t method_idx = DexMethodIndex(inst.Inst());
            ++types_accessed[dex_file.GetMethodId(method_idx).class_idx_.index_];
            if (dex_file.GetMethodId(method_idx).class_idx_ == accessor.GetClassIdx()) {
              ++same_class_direct_;
            }
            ++total_direct_;
            unique_method_ids.insert(method_idx);
            break;
          }
          case Instruction::INVOKE_STATIC:
          case Instruction::INVOKE_STATIC_RANGE: {
            uint32_t method_idx = DexMethodIndex(inst.Inst());
            ++types_accessed[dex_file.GetMethodId(method_idx).class_idx_.index_];
            if (dex_file.GetMethodId(method_idx).class_idx_ == accessor.GetClassIdx()) {
              ++same_class_static_;
            }
            ++total_static_;
            unique_method_ids.insert(method_idx);
            break;
          }
          case Instruction::INVOKE_INTERFACE:
          case Instruction::INVOKE_INTERFACE_RANGE: {
            uint32_t method_idx = DexMethodIndex(inst.Inst());
            ++types_accessed[dex_file.GetMethodId(method_idx).class_idx_.index_];
            if (dex_file.GetMethodId(method_idx).class_idx_ == accessor.GetClassIdx()) {
              ++same_class_interface_;
            }
            ++total_interface_;
            unique_method_ids.insert(method_idx);
            break;
          }
          case Instruction::INVOKE_SUPER:
          case Instruction::INVOKE_SUPER_RANGE: {
            uint32_t method_idx = DexMethodIndex(inst.Inst());
            ++types_accessed[dex_file.GetMethodId(method_idx).class_idx_.index_];
            if (dex_file.GetMethodId(method_idx).class_idx_ == accessor.GetClassIdx()) {
              ++same_class_super_;
            }
            ++total_super_;
            unique_method_ids.insert(method_idx);
            break;
          }
          case Instruction::NEW_ARRAY: {
            ++types_accessed[inst->VRegC_22c()];
            break;
          }
          case Instruction::FILLED_NEW_ARRAY: {
            ++types_accessed[inst->VRegB_35c()];
            break;
          }
          case Instruction::FILLED_NEW_ARRAY_RANGE: {
            ++types_accessed[inst->VRegB_3rc()];
            break;
          }
          case Instruction::CONST_CLASS:
          case Instruction::CHECK_CAST:
          case Instruction::NEW_INSTANCE: {
            ++types_accessed[inst->VRegB_21c()];
            break;
          }
          case Instruction::INSTANCE_OF: {
            ++types_accessed[inst->VRegB_21c()];
            break;
          }
          default:
            break;
        }
      }
    }
    // Count uses of top 16n.
    std::vector<size_t> uses;
    uses.reserve(types_accessed.size());
    for (auto&& p : types_accessed) {
      uses.push_back(p.second);
    }
    std::sort(uses.rbegin(), uses.rend());
    for (size_t i = 0; i < uses.size(); ++i) {
      if (i < 16) {
        uses_top_types_ += uses[i];
      }
      uses_all_types_ += uses[i];
    }
    total_unique_types_ += types_accessed.size();
    total_unique_method_ids_ += unique_method_ids.size();
    total_unique_string_ids_ += unique_string_ids.size();
  }
  total_unique_code_items_ += unique_code_items.size();
}

void CountDexIndices::Dump(std::ostream& os, uint64_t total_size) const {
  auto DumpFieldIndexes = [&](const FieldAccessStats& stats) {
    const uint64_t fields_idx_total = std::accumulate(
        stats.field_index_,
        stats.field_index_ + FieldAccessStats::kMaxFieldIndex,
        stats.field_index_other_ + stats.field_index_other_class_);
    for (size_t i = 0; i < FieldAccessStats::kMaxFieldIndex; ++i) {
      os << "  field_idx=" << i << ": " << Percent(stats.field_index_[i], fields_idx_total) << "\n";
    }
    os << "  field_idx=other: " << Percent(stats.field_index_other_, fields_idx_total) << "\n";
    os << "  field_idx=other_class: " << Percent(stats.field_index_other_class_, fields_idx_total)
       << "\n";
  };
  auto DumpInstanceFieldStats = [&](const char* tag, const InstanceFieldAccessStats& stats) {
    const uint64_t fields_total = std::accumulate(stats.inout_, stats.inout_ + 16u, 0u);
    os << tag << "\n";
    for (size_t i = 0; i < 16; ++i) {
      os << "  receiver_reg=" << i << ": " << Percent(stats.receiver_[i], fields_total) << "\n";
    }
    DCHECK(tag[1] == 'G' || tag[1] == 'P');
    const char* inout_tag = (tag[1] == 'G') ? "output_reg" : "input_reg";
    for (size_t i = 0; i < 16; ++i) {
      os << "  " << inout_tag << "=" << i << ": " << Percent(stats.inout_[i], fields_total) << "\n";
    }
    DumpFieldIndexes(stats);
    os << "  short_bytecode: " << Percent(stats.short_bytecode_, fields_total) << "\n";
    os << "  short_bytecode_savings=" << Percent(stats.short_bytecode_ * 2, total_size) << "\n";
  };
  DumpInstanceFieldStats("IGET", iget_stats_);
  DumpInstanceFieldStats("IPUT", iput_stats_);

  auto DumpStaticFieldStats = [&](const char* tag, const StaticFieldAccessStats& stats) {
    const uint64_t fields_total =
        std::accumulate(stats.inout_, stats.inout_ + 16u, stats.inout_other_);
    os << tag << "\n";
    DCHECK(tag[1] == 'G' || tag[1] == 'P');
    const char* inout_tag = (tag[1] == 'G') ? "output_reg" : "input_reg";
    for (size_t i = 0; i < 16; ++i) {
      os << "  " << inout_tag << "=" << i << ": " << Percent(stats.inout_[i], fields_total) << "\n";
    }
    os << "  " << inout_tag << "=other: " << Percent(stats.inout_other_, fields_total) << "\n";
    DumpFieldIndexes(stats);
    os << "  short_bytecode: " << Percent(stats.short_bytecode_, fields_total) << "\n";
    os << "  short_bytecode_savings=" << Percent(stats.short_bytecode_ * 2, total_size) << "\n";
  };
  DumpStaticFieldStats("SGET", sget_stats_);
  DumpStaticFieldStats("SPUT", sput_stats_);

  os << "Num string ids: " << num_string_ids_ << "\n";
  os << "Num method ids: " << num_method_ids_ << "\n";
  os << "Num field ids: " << num_field_ids_ << "\n";
  os << "Num type ids: " << num_type_ids_ << "\n";
  os << "Num class defs: " << num_class_defs_ << "\n";
  os << "Direct same class: " << PercentDivide(same_class_direct_, total_direct_) << "\n";
  os << "Virtual same class: " << PercentDivide(same_class_virtual_, total_virtual_) << "\n";
  os << "Static same class: " << PercentDivide(same_class_static_, total_static_) << "\n";
  os << "Interface same class: " << PercentDivide(same_class_interface_, total_interface_) << "\n";
  os << "Super same class: " << PercentDivide(same_class_super_, total_super_) << "\n";
  os << "Num strings accessed from code: " << num_string_ids_from_code_ << "\n";
  os << "Avg unique methods accessed per class: "
     << static_cast<double>(total_unique_method_ids_) / static_cast<double>(num_class_defs_) << "\n";
  os << "Avg unique strings accessed per class: "
     << static_cast<double>(total_unique_string_ids_) / static_cast<double>(num_class_defs_) << "\n";
  os << "Avg unique types accessed per class " <<
        static_cast<double>(total_unique_types_) / static_cast<double>(num_class_defs_) << "\n";
  os << "Total unique methods accessed per class: "
     << Percent(total_unique_method_ids_, total_size) << "\n";
  os << "Total unique strings accessed per class: "
     << Percent(total_unique_string_ids_, total_size) << "\n";
  os << "Total unique types accessed per class: "
     << Percent(total_unique_types_, total_size) << "\n";
  const size_t same_class_total =
      same_class_direct_ +
      same_class_virtual_ +
      same_class_static_ +
      same_class_interface_ +
      same_class_super_;
  const size_t other_class_total =
      total_direct_ +
      total_virtual_ +
      total_static_ +
      total_interface_ +
      total_super_;
  os << "Unique method names: " << Percent(total_unique_method_names_, num_field_ids_) << "\n";
  os << "Unique field names: " << Percent(total_unique_field_names_, num_method_ids_) << "\n";
  os << "Unique type names: " << Percent(total_unique_type_names_, num_type_ids_) << "\n";
  os << "Unique method/field names: "
     << Percent(total_unique_mf_names_, num_field_ids_ + num_method_ids_) << "\n";
  os << "Same class invokes: " << PercentDivide(same_class_total, other_class_total) << "\n";
  os << "Invokes from code: " << (same_class_total + other_class_total) << "\n";
  os << "Type uses on top types: " << PercentDivide(uses_top_types_, uses_all_types_) << "\n";
  os << "Type uses 1b savings: " << PercentDivide(uses_top_types_, total_size) << "\n";
  os << "Total Dex code bytes: " << Percent(dex_code_bytes_, total_size) << "\n";
  os << "Total unique code items: " << total_unique_code_items_ << "\n";
  os << "Total Dex size: " << total_size << "\n";
}

void CodeMetrics::ProcessDexFile(const DexFile& dex_file) {
  for (ClassAccessor accessor : dex_file.GetClasses()) {
    for (const ClassAccessor::Method& method : accessor.GetMethods()) {
      bool space_for_out_arg = false;
      for (const DexInstructionPcPair& inst : method.GetInstructions()) {
        switch (inst->Opcode()) {
          case Instruction::INVOKE_VIRTUAL:
          case Instruction::INVOKE_DIRECT:
          case Instruction::INVOKE_SUPER:
          case Instruction::INVOKE_INTERFACE:
          case Instruction::INVOKE_STATIC: {
            const uint32_t args = NumberOfArgs(inst.Inst());
            CHECK_LT(args, kMaxArgCount);
            ++arg_counts_[args];
            space_for_out_arg = args < kMaxArgCount - 1;
            break;
          }
          case Instruction::MOVE_RESULT:
          case Instruction::MOVE_RESULT_OBJECT: {
            if (space_for_out_arg && inst->VRegA_11x() < 16) {
              move_result_savings_ += inst->SizeInCodeUnits() * 2;
            }
            break;
          }
          default:
            space_for_out_arg = false;
            break;
        }
      }
    }
  }
}

void CodeMetrics::Dump(std::ostream& os, uint64_t total_size) const {
  const uint64_t total = std::accumulate(arg_counts_, arg_counts_ + kMaxArgCount, 0u);
  for (size_t i = 0; i < kMaxArgCount; ++i) {
    os << "args=" << i << ": " << Percent(arg_counts_[i], total) << "\n";
  }
  os << "Move result savings: " << Percent(move_result_savings_, total_size) << "\n";
  os << "One byte invoke savings: " << Percent(total, total_size) << "\n";
  const uint64_t low_arg_total = std::accumulate(arg_counts_, arg_counts_ + 20u);
  os << "Low arg savings: " << Percent(low_arg_total * 2, total_size) << "\n";
}

}  // namespace dexanalyze
}  // namespace art

Messung V0.5 in Prozent
C=82 H=86 G=83

¤ Dauer der Verarbeitung: 0.12 Sekunden  (vorverarbeitet am  2026-06-29) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik