• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include <android-base/file.h>
18 
19 #include <cstdint>
20 #include <iostream>
21 #include <set>
22 #include <sstream>
23 
24 #include "base/mem_map.h"
25 #include "dex/code_item_accessors-inl.h"
26 #include "dex/dex_file.h"
27 #include "dex/dex_file_loader.h"
28 #include "dex/dex_instruction-inl.h"
29 #include "dexanalyze_bytecode.h"
30 #include "dexanalyze_experiments.h"
31 #include "dexanalyze_strings.h"
32 
33 namespace art {
34 namespace dexanalyze {
35 
36 class DexAnalyze {
37   static constexpr int kExitCodeUsageError = 1;
38   static constexpr int kExitCodeFailedToOpenFile = 2;
39   static constexpr int kExitCodeFailedToOpenDex = 3;
40   static constexpr int kExitCodeFailedToProcessDex = 4;
41 
StdoutLogger(android::base::LogId,android::base::LogSeverity,const char *,const char *,unsigned int,const char * message)42   static void StdoutLogger(android::base::LogId,
43                            android::base::LogSeverity,
44                            const char*,
45                            const char*,
46                            unsigned int,
47                            const char* message) {
48     std::cout << message << std::endl;
49   }
50 
Usage(char ** argv)51   static int Usage(char** argv) {
52     LOG(ERROR)
53         << "Usage " << argv[0] << " [options] <dex files>\n"
54         << "    [options] is a combination of the following\n"
55         << "    -count-indices (Count dex indices accessed from code items)\n"
56         << "    -analyze-strings (Analyze string data)\n"
57         << "    -analyze-debug-info (Analyze debug info)\n"
58         << "    -new-bytecode (Bytecode optimizations)\n"
59         << "    -i (Ignore Dex checksum and verification failures)\n"
60         << "    -a (Run all experiments)\n"
61         << "    -n <int> (run experiment with 1 .. n as argument)\n"
62         << "    -d (Dump on per Dex basis)\n"
63         << "    -v (quiet(0) to everything(2))\n";
64     return kExitCodeUsageError;
65   }
66 
67   struct Options {
Parseart::dexanalyze::DexAnalyze::Options68     int Parse(int argc, char** argv) {
69       int i;
70       for (i = 1; i < argc; ++i) {
71         const std::string arg = argv[i];
72         if (arg == "-i") {
73           verify_checksum_ = false;
74           run_dex_file_verifier_ = false;
75         } else if (arg == "-v") {
76           if (i + 1 >= argc) {
77             return Usage(argv);
78           }
79           std::istringstream iss(argv[i + 1]);
80           size_t verbose_level = 0u;
81           iss >> verbose_level;
82           if (verbose_level > static_cast<size_t>(VerboseLevel::kEverything)) {
83             return Usage(argv);
84           }
85           ++i;
86           verbose_level_ = static_cast<VerboseLevel>(verbose_level);
87         } else if (arg == "-a") {
88           run_all_experiments_ = true;
89         } else if (arg == "-n") {
90           if (i + 1 >= argc) {
91             return Usage(argv);
92           }
93           std::istringstream iss(argv[i + 1]);
94           iss >> experiment_max_;
95           ++i;
96         } else if (arg == "-count-indices") {
97           exp_count_indices_ = true;
98         } else if (arg == "-analyze-strings") {
99           exp_analyze_strings_ = true;
100         } else if (arg == "-analyze-debug-info") {
101           exp_debug_info_ = true;
102         } else if (arg == "-new-bytecode") {
103           exp_bytecode_ = true;
104         } else if (arg == "-d") {
105           dump_per_input_dex_ = true;
106         } else if (!arg.empty() && arg[0] == '-') {
107           return Usage(argv);
108         } else {
109           break;
110         }
111       }
112       filenames_.insert(filenames_.end(), argv + i, argv + argc);
113       if (filenames_.empty()) {
114         return Usage(argv);
115       }
116       return 0;
117     }
118 
119     VerboseLevel verbose_level_ = VerboseLevel::kNormal;
120     bool verify_checksum_ = true;
121     bool run_dex_file_verifier_ = true;
122     bool dump_per_input_dex_ = false;
123     bool exp_count_indices_ = false;
124     bool exp_code_metrics_ = false;
125     bool exp_analyze_strings_ = false;
126     bool exp_debug_info_ = false;
127     bool exp_bytecode_ = false;
128     bool run_all_experiments_ = false;
129     uint64_t experiment_max_ = 1u;
130     std::vector<std::string> filenames_;
131   };
132 
133   class Analysis {
134    public:
Analysis(const Options * options)135     explicit Analysis(const Options* options) : options_(options) {
136       if (options->run_all_experiments_ || options->exp_count_indices_) {
137         experiments_.emplace_back(new CountDexIndices);
138       }
139       if (options->run_all_experiments_ || options->exp_analyze_strings_) {
140         experiments_.emplace_back(new AnalyzeStrings);
141       }
142       if (options->run_all_experiments_ || options->exp_code_metrics_) {
143         experiments_.emplace_back(new CodeMetrics);
144       }
145       if (options->run_all_experiments_ || options->exp_debug_info_) {
146         experiments_.emplace_back(new AnalyzeDebugInfo);
147       }
148       if (options->run_all_experiments_ || options->exp_bytecode_) {
149         for (size_t i = 0; i < options->experiment_max_; ++i) {
150           uint64_t exp_value = 0u;
151           if (i == 0) {
152             exp_value = std::numeric_limits<uint64_t>::max();
153           } else if (i == 1) {
154             exp_value = 0u;
155           } else {
156             exp_value = 1u << (i - 2);
157           }
158           experiments_.emplace_back(new NewRegisterInstructions(exp_value));
159         }
160       }
161       for (const std::unique_ptr<Experiment>& experiment : experiments_) {
162         experiment->verbose_level_ = options->verbose_level_;
163       }
164     }
165 
ProcessDexFiles(const std::vector<std::unique_ptr<const DexFile>> & dex_files)166     bool ProcessDexFiles(const std::vector<std::unique_ptr<const DexFile>>& dex_files) {
167       for (std::unique_ptr<Experiment>& experiment : experiments_) {
168         experiment->ProcessDexFiles(dex_files);
169       }
170       for (const std::unique_ptr<const DexFile>& dex_file : dex_files) {
171         total_size_ += dex_file->Size();
172       }
173       dex_count_ += dex_files.size();
174       return true;
175     }
176 
Dump(std::ostream & os)177     void Dump(std::ostream& os) {
178       for (std::unique_ptr<Experiment>& experiment : experiments_) {
179         experiment->Dump(os, total_size_);
180         os << "\n";
181       }
182     }
183 
184     const Options* const options_;
185     std::vector<std::unique_ptr<Experiment>> experiments_;
186     size_t dex_count_ = 0;
187     uint64_t total_size_ = 0u;
188   };
189 
190  public:
Run(int argc,char ** argv)191   static int Run(int argc, char** argv) {
192     android::base::SetLogger(StdoutLogger);
193 
194     Options options;
195     int result = options.Parse(argc, argv);
196     if (result != 0) {
197       return result;
198     }
199 
200     DexFileLoaderErrorCode error_code;
201     std::string error_msg;
202     Analysis cumulative(&options);
203     for (const std::string& filename : options.filenames_) {
204       std::string content;
205       // TODO: once added, use an API to android::base to read a std::vector<uint8_t>.
206       if (!android::base::ReadFileToString(filename, &content)) {
207         LOG(ERROR) << "ReadFileToString failed for " + filename << std::endl;
208         return kExitCodeFailedToOpenFile;
209       }
210       std::vector<std::unique_ptr<const DexFile>> dex_files;
211       DexFileLoader dex_file_loader(
212           reinterpret_cast<const uint8_t*>(content.data()), content.size(), filename);
213       if (!dex_file_loader.Open(options.run_dex_file_verifier_,
214                                 options.verify_checksum_,
215                                 &error_code,
216                                 &error_msg,
217                                 &dex_files)) {
218         LOG(ERROR) << "OpenAll failed for " + filename << " with " << error_msg << std::endl;
219         return kExitCodeFailedToOpenDex;
220       }
221       if (options.dump_per_input_dex_) {
222         Analysis current(&options);
223         if (!current.ProcessDexFiles(dex_files)) {
224           LOG(ERROR) << "Failed to process " << filename << " with error " << error_msg;
225           return kExitCodeFailedToProcessDex;
226         }
227         LOG(INFO) << "Analysis for " << filename << std::endl;
228         current.Dump(LOG_STREAM(INFO));
229       }
230       cumulative.ProcessDexFiles(dex_files);
231     }
232     LOG(INFO) << "Cumulative analysis for " << cumulative.dex_count_ << " DEX files" << std::endl;
233     cumulative.Dump(LOG_STREAM(INFO));
234     return 0;
235   }
236 };
237 
238 }  // namespace dexanalyze
239 }  // namespace art
240 
main(int argc,char ** argv)241 int main(int argc, char** argv) {
242   art::MemMap::Init();
243   return art::dexanalyze::DexAnalyze::Run(argc, argv);
244 }
245 
246