xref: /aosp_15_r20/art/dexdump/dexdump_cfg.cc (revision 795d594fd825385562da6b089ea9b2033f3abf5a)
1*795d594fSAndroid Build Coastguard Worker /*
2*795d594fSAndroid Build Coastguard Worker  * Copyright (C) 2016 The Android Open Source Project
3*795d594fSAndroid Build Coastguard Worker  *
4*795d594fSAndroid Build Coastguard Worker  * Licensed under the Apache License, Version 2.0 (the "License");
5*795d594fSAndroid Build Coastguard Worker  * you may not use this file except in compliance with the License.
6*795d594fSAndroid Build Coastguard Worker  * You may obtain a copy of the License at
7*795d594fSAndroid Build Coastguard Worker  *
8*795d594fSAndroid Build Coastguard Worker  *      http://www.apache.org/licenses/LICENSE-2.0
9*795d594fSAndroid Build Coastguard Worker  *
10*795d594fSAndroid Build Coastguard Worker  * Unless required by applicable law or agreed to in writing, software
11*795d594fSAndroid Build Coastguard Worker  * distributed under the License is distributed on an "AS IS" BASIS,
12*795d594fSAndroid Build Coastguard Worker  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*795d594fSAndroid Build Coastguard Worker  * See the License for the specific language governing permissions and
14*795d594fSAndroid Build Coastguard Worker  * limitations under the License.
15*795d594fSAndroid Build Coastguard Worker  *
16*795d594fSAndroid Build Coastguard Worker  * Implementation file for control flow graph dumping for the dexdump utility.
17*795d594fSAndroid Build Coastguard Worker  */
18*795d594fSAndroid Build Coastguard Worker 
19*795d594fSAndroid Build Coastguard Worker #include "dexdump_cfg.h"
20*795d594fSAndroid Build Coastguard Worker 
21*795d594fSAndroid Build Coastguard Worker #include <inttypes.h>
22*795d594fSAndroid Build Coastguard Worker 
23*795d594fSAndroid Build Coastguard Worker #include <map>
24*795d594fSAndroid Build Coastguard Worker #include <ostream>
25*795d594fSAndroid Build Coastguard Worker #include <set>
26*795d594fSAndroid Build Coastguard Worker #include <sstream>
27*795d594fSAndroid Build Coastguard Worker 
28*795d594fSAndroid Build Coastguard Worker #include "dex/class_accessor-inl.h"
29*795d594fSAndroid Build Coastguard Worker #include "dex/code_item_accessors-inl.h"
30*795d594fSAndroid Build Coastguard Worker #include "dex/dex_file-inl.h"
31*795d594fSAndroid Build Coastguard Worker #include "dex/dex_file_exception_helpers.h"
32*795d594fSAndroid Build Coastguard Worker #include "dex/dex_instruction-inl.h"
33*795d594fSAndroid Build Coastguard Worker 
34*795d594fSAndroid Build Coastguard Worker namespace art {
35*795d594fSAndroid Build Coastguard Worker 
DumpMethodCFG(const ClassAccessor::Method & method,std::ostream & os)36*795d594fSAndroid Build Coastguard Worker void DumpMethodCFG(const ClassAccessor::Method& method, std::ostream& os) {
37*795d594fSAndroid Build Coastguard Worker   const DexFile* dex_file = &method.GetDexFile();
38*795d594fSAndroid Build Coastguard Worker   os << "digraph {\n";
39*795d594fSAndroid Build Coastguard Worker   os << "  # /* " << dex_file->PrettyMethod(method.GetIndex(), true) << " */\n";
40*795d594fSAndroid Build Coastguard Worker 
41*795d594fSAndroid Build Coastguard Worker   CodeItemDataAccessor accessor(method.GetInstructionsAndData());
42*795d594fSAndroid Build Coastguard Worker   std::set<uint32_t> dex_pc_is_branch_target;
43*795d594fSAndroid Build Coastguard Worker   {
44*795d594fSAndroid Build Coastguard Worker     // Go and populate.
45*795d594fSAndroid Build Coastguard Worker     for (const DexInstructionPcPair& pair : accessor) {
46*795d594fSAndroid Build Coastguard Worker       const Instruction* inst = &pair.Inst();
47*795d594fSAndroid Build Coastguard Worker       if (inst->IsBranch()) {
48*795d594fSAndroid Build Coastguard Worker         dex_pc_is_branch_target.insert(pair.DexPc() + inst->GetTargetOffset());
49*795d594fSAndroid Build Coastguard Worker       } else if (inst->IsSwitch()) {
50*795d594fSAndroid Build Coastguard Worker         const uint16_t* insns = reinterpret_cast<const uint16_t*>(inst);
51*795d594fSAndroid Build Coastguard Worker         int32_t switch_offset = insns[1] | (static_cast<int32_t>(insns[2]) << 16);
52*795d594fSAndroid Build Coastguard Worker         const uint16_t* switch_insns = insns + switch_offset;
53*795d594fSAndroid Build Coastguard Worker         uint32_t switch_count = switch_insns[1];
54*795d594fSAndroid Build Coastguard Worker         int32_t targets_offset;
55*795d594fSAndroid Build Coastguard Worker         if ((*insns & 0xff) == Instruction::PACKED_SWITCH) {
56*795d594fSAndroid Build Coastguard Worker           /* 0=sig, 1=count, 2/3=firstKey */
57*795d594fSAndroid Build Coastguard Worker           targets_offset = 4;
58*795d594fSAndroid Build Coastguard Worker         } else {
59*795d594fSAndroid Build Coastguard Worker           /* 0=sig, 1=count, 2..count*2 = keys */
60*795d594fSAndroid Build Coastguard Worker           targets_offset = 2 + 2 * switch_count;
61*795d594fSAndroid Build Coastguard Worker         }
62*795d594fSAndroid Build Coastguard Worker         for (uint32_t targ = 0; targ < switch_count; targ++) {
63*795d594fSAndroid Build Coastguard Worker           int32_t offset =
64*795d594fSAndroid Build Coastguard Worker               static_cast<int32_t>(switch_insns[targets_offset + targ * 2]) |
65*795d594fSAndroid Build Coastguard Worker               static_cast<int32_t>(switch_insns[targets_offset + targ * 2 + 1] << 16);
66*795d594fSAndroid Build Coastguard Worker           dex_pc_is_branch_target.insert(pair.DexPc() + offset);
67*795d594fSAndroid Build Coastguard Worker         }
68*795d594fSAndroid Build Coastguard Worker       }
69*795d594fSAndroid Build Coastguard Worker     }
70*795d594fSAndroid Build Coastguard Worker   }
71*795d594fSAndroid Build Coastguard Worker 
72*795d594fSAndroid Build Coastguard Worker   // Create nodes for "basic blocks."
73*795d594fSAndroid Build Coastguard Worker   std::map<uint32_t, uint32_t> dex_pc_to_node_id;  // This only has entries for block starts.
74*795d594fSAndroid Build Coastguard Worker   std::map<uint32_t, uint32_t> dex_pc_to_incl_id;  // This has entries for all dex pcs.
75*795d594fSAndroid Build Coastguard Worker 
76*795d594fSAndroid Build Coastguard Worker   {
77*795d594fSAndroid Build Coastguard Worker     bool first_in_block = true;
78*795d594fSAndroid Build Coastguard Worker     bool force_new_block = false;
79*795d594fSAndroid Build Coastguard Worker     for (const DexInstructionPcPair& pair : accessor) {
80*795d594fSAndroid Build Coastguard Worker       const uint32_t dex_pc = pair.DexPc();
81*795d594fSAndroid Build Coastguard Worker       if (dex_pc == 0 ||
82*795d594fSAndroid Build Coastguard Worker           (dex_pc_is_branch_target.find(dex_pc) != dex_pc_is_branch_target.end()) ||
83*795d594fSAndroid Build Coastguard Worker           force_new_block) {
84*795d594fSAndroid Build Coastguard Worker         uint32_t id = dex_pc_to_node_id.size();
85*795d594fSAndroid Build Coastguard Worker         if (id > 0) {
86*795d594fSAndroid Build Coastguard Worker           // End last node.
87*795d594fSAndroid Build Coastguard Worker           os << "}\"];\n";
88*795d594fSAndroid Build Coastguard Worker         }
89*795d594fSAndroid Build Coastguard Worker         // Start next node.
90*795d594fSAndroid Build Coastguard Worker         os << "  node" << id << " [shape=record,label=\"{";
91*795d594fSAndroid Build Coastguard Worker         dex_pc_to_node_id.insert(std::make_pair(dex_pc, id));
92*795d594fSAndroid Build Coastguard Worker         first_in_block = true;
93*795d594fSAndroid Build Coastguard Worker         force_new_block = false;
94*795d594fSAndroid Build Coastguard Worker       }
95*795d594fSAndroid Build Coastguard Worker 
96*795d594fSAndroid Build Coastguard Worker       // Register instruction.
97*795d594fSAndroid Build Coastguard Worker       dex_pc_to_incl_id.insert(std::make_pair(dex_pc, dex_pc_to_node_id.size() - 1));
98*795d594fSAndroid Build Coastguard Worker 
99*795d594fSAndroid Build Coastguard Worker       // Print instruction.
100*795d594fSAndroid Build Coastguard Worker       if (!first_in_block) {
101*795d594fSAndroid Build Coastguard Worker         os << " | ";
102*795d594fSAndroid Build Coastguard Worker       } else {
103*795d594fSAndroid Build Coastguard Worker         first_in_block = false;
104*795d594fSAndroid Build Coastguard Worker       }
105*795d594fSAndroid Build Coastguard Worker 
106*795d594fSAndroid Build Coastguard Worker       // Dump the instruction. Need to escape '"', '<', '>', '{' and '}'.
107*795d594fSAndroid Build Coastguard Worker       os << "<" << "p" << dex_pc << ">";
108*795d594fSAndroid Build Coastguard Worker       os << " 0x" << std::hex << dex_pc << std::dec << ": ";
109*795d594fSAndroid Build Coastguard Worker       std::string inst_str = pair.Inst().DumpString(dex_file);
110*795d594fSAndroid Build Coastguard Worker       size_t cur_start = 0;  // It's OK to start at zero, instruction dumps don't start with chars
111*795d594fSAndroid Build Coastguard Worker                              // we need to escape.
112*795d594fSAndroid Build Coastguard Worker       while (cur_start != std::string::npos) {
113*795d594fSAndroid Build Coastguard Worker         size_t next_escape = inst_str.find_first_of("\"{}<>", cur_start + 1);
114*795d594fSAndroid Build Coastguard Worker         if (next_escape == std::string::npos) {
115*795d594fSAndroid Build Coastguard Worker           os << inst_str.substr(cur_start, inst_str.size() - cur_start);
116*795d594fSAndroid Build Coastguard Worker           break;
117*795d594fSAndroid Build Coastguard Worker         } else {
118*795d594fSAndroid Build Coastguard Worker           os << inst_str.substr(cur_start, next_escape - cur_start);
119*795d594fSAndroid Build Coastguard Worker           // Escape all necessary characters.
120*795d594fSAndroid Build Coastguard Worker           while (next_escape < inst_str.size()) {
121*795d594fSAndroid Build Coastguard Worker             char c = inst_str[next_escape];
122*795d594fSAndroid Build Coastguard Worker             if (c == '"' || c == '{' || c == '}' || c == '<' || c == '>') {
123*795d594fSAndroid Build Coastguard Worker               os << '\\' << c;
124*795d594fSAndroid Build Coastguard Worker             } else {
125*795d594fSAndroid Build Coastguard Worker               break;
126*795d594fSAndroid Build Coastguard Worker             }
127*795d594fSAndroid Build Coastguard Worker             next_escape++;
128*795d594fSAndroid Build Coastguard Worker           }
129*795d594fSAndroid Build Coastguard Worker           if (next_escape >= inst_str.size()) {
130*795d594fSAndroid Build Coastguard Worker             next_escape = std::string::npos;
131*795d594fSAndroid Build Coastguard Worker           }
132*795d594fSAndroid Build Coastguard Worker           cur_start = next_escape;
133*795d594fSAndroid Build Coastguard Worker         }
134*795d594fSAndroid Build Coastguard Worker       }
135*795d594fSAndroid Build Coastguard Worker 
136*795d594fSAndroid Build Coastguard Worker       // Force a new block for some fall-throughs and some instructions that terminate the "local"
137*795d594fSAndroid Build Coastguard Worker       // control flow.
138*795d594fSAndroid Build Coastguard Worker       force_new_block = pair.Inst().IsSwitch() || pair.Inst().IsBasicBlockEnd();
139*795d594fSAndroid Build Coastguard Worker     }
140*795d594fSAndroid Build Coastguard Worker     // Close last node.
141*795d594fSAndroid Build Coastguard Worker     if (dex_pc_to_node_id.size() > 0) {
142*795d594fSAndroid Build Coastguard Worker       os << "}\"];\n";
143*795d594fSAndroid Build Coastguard Worker     }
144*795d594fSAndroid Build Coastguard Worker   }
145*795d594fSAndroid Build Coastguard Worker 
146*795d594fSAndroid Build Coastguard Worker   // Create edges between them.
147*795d594fSAndroid Build Coastguard Worker   {
148*795d594fSAndroid Build Coastguard Worker     std::ostringstream regular_edges;
149*795d594fSAndroid Build Coastguard Worker     std::ostringstream taken_edges;
150*795d594fSAndroid Build Coastguard Worker     std::ostringstream exception_edges;
151*795d594fSAndroid Build Coastguard Worker 
152*795d594fSAndroid Build Coastguard Worker     // Common set of exception edges.
153*795d594fSAndroid Build Coastguard Worker     std::set<uint32_t> exception_targets;
154*795d594fSAndroid Build Coastguard Worker 
155*795d594fSAndroid Build Coastguard Worker     // These blocks (given by the first dex pc) need exception per dex-pc handling in a second
156*795d594fSAndroid Build Coastguard Worker     // pass. In the first pass we try and see whether we can use a common set of edges.
157*795d594fSAndroid Build Coastguard Worker     std::set<uint32_t> blocks_with_detailed_exceptions;
158*795d594fSAndroid Build Coastguard Worker 
159*795d594fSAndroid Build Coastguard Worker     {
160*795d594fSAndroid Build Coastguard Worker       uint32_t last_node_id = std::numeric_limits<uint32_t>::max();
161*795d594fSAndroid Build Coastguard Worker       uint32_t old_dex_pc = 0;
162*795d594fSAndroid Build Coastguard Worker       uint32_t block_start_dex_pc = std::numeric_limits<uint32_t>::max();
163*795d594fSAndroid Build Coastguard Worker       for (const DexInstructionPcPair& pair : accessor) {
164*795d594fSAndroid Build Coastguard Worker         const Instruction* inst = &pair.Inst();
165*795d594fSAndroid Build Coastguard Worker         const uint32_t dex_pc = pair.DexPc();
166*795d594fSAndroid Build Coastguard Worker         {
167*795d594fSAndroid Build Coastguard Worker           auto it = dex_pc_to_node_id.find(dex_pc);
168*795d594fSAndroid Build Coastguard Worker           if (it != dex_pc_to_node_id.end()) {
169*795d594fSAndroid Build Coastguard Worker             if (!exception_targets.empty()) {
170*795d594fSAndroid Build Coastguard Worker               // It seems the last block had common exception handlers. Add the exception edges now.
171*795d594fSAndroid Build Coastguard Worker               uint32_t node_id = dex_pc_to_node_id.find(block_start_dex_pc)->second;
172*795d594fSAndroid Build Coastguard Worker               for (uint32_t handler_pc : exception_targets) {
173*795d594fSAndroid Build Coastguard Worker                 auto node_id_it = dex_pc_to_incl_id.find(handler_pc);
174*795d594fSAndroid Build Coastguard Worker                 if (node_id_it != dex_pc_to_incl_id.end()) {
175*795d594fSAndroid Build Coastguard Worker                   exception_edges << "  node" << node_id
176*795d594fSAndroid Build Coastguard Worker                       << " -> node" << node_id_it->second << ":p" << handler_pc
177*795d594fSAndroid Build Coastguard Worker                       << ";\n";
178*795d594fSAndroid Build Coastguard Worker                 }
179*795d594fSAndroid Build Coastguard Worker               }
180*795d594fSAndroid Build Coastguard Worker               exception_targets.clear();
181*795d594fSAndroid Build Coastguard Worker             }
182*795d594fSAndroid Build Coastguard Worker 
183*795d594fSAndroid Build Coastguard Worker             block_start_dex_pc = dex_pc;
184*795d594fSAndroid Build Coastguard Worker 
185*795d594fSAndroid Build Coastguard Worker             // Seems to be a fall-through, connect to last_node_id. May be spurious edges for things
186*795d594fSAndroid Build Coastguard Worker             // like switch data.
187*795d594fSAndroid Build Coastguard Worker             uint32_t old_last = last_node_id;
188*795d594fSAndroid Build Coastguard Worker             last_node_id = it->second;
189*795d594fSAndroid Build Coastguard Worker             if (old_last != std::numeric_limits<uint32_t>::max()) {
190*795d594fSAndroid Build Coastguard Worker               regular_edges << "  node" << old_last << ":p" << old_dex_pc
191*795d594fSAndroid Build Coastguard Worker                   << " -> node" << last_node_id << ":p" << dex_pc
192*795d594fSAndroid Build Coastguard Worker                   << ";\n";
193*795d594fSAndroid Build Coastguard Worker             }
194*795d594fSAndroid Build Coastguard Worker           }
195*795d594fSAndroid Build Coastguard Worker 
196*795d594fSAndroid Build Coastguard Worker           // Look at the exceptions of the first entry.
197*795d594fSAndroid Build Coastguard Worker           CatchHandlerIterator catch_it(accessor, dex_pc);
198*795d594fSAndroid Build Coastguard Worker           for (; catch_it.HasNext(); catch_it.Next()) {
199*795d594fSAndroid Build Coastguard Worker             exception_targets.insert(catch_it.GetHandlerAddress());
200*795d594fSAndroid Build Coastguard Worker           }
201*795d594fSAndroid Build Coastguard Worker         }
202*795d594fSAndroid Build Coastguard Worker 
203*795d594fSAndroid Build Coastguard Worker         // Handle instruction.
204*795d594fSAndroid Build Coastguard Worker 
205*795d594fSAndroid Build Coastguard Worker         // Branch: something with at most two targets.
206*795d594fSAndroid Build Coastguard Worker         if (inst->IsBranch()) {
207*795d594fSAndroid Build Coastguard Worker           const int32_t offset = inst->GetTargetOffset();
208*795d594fSAndroid Build Coastguard Worker           const bool conditional = !inst->IsUnconditional();
209*795d594fSAndroid Build Coastguard Worker 
210*795d594fSAndroid Build Coastguard Worker           auto target_it = dex_pc_to_node_id.find(dex_pc + offset);
211*795d594fSAndroid Build Coastguard Worker           if (target_it != dex_pc_to_node_id.end()) {
212*795d594fSAndroid Build Coastguard Worker             taken_edges << "  node" << last_node_id << ":p" << dex_pc
213*795d594fSAndroid Build Coastguard Worker                 << " -> node" << target_it->second << ":p" << (dex_pc + offset)
214*795d594fSAndroid Build Coastguard Worker                 << ";\n";
215*795d594fSAndroid Build Coastguard Worker           }
216*795d594fSAndroid Build Coastguard Worker           if (!conditional) {
217*795d594fSAndroid Build Coastguard Worker             // No fall-through.
218*795d594fSAndroid Build Coastguard Worker             last_node_id = std::numeric_limits<uint32_t>::max();
219*795d594fSAndroid Build Coastguard Worker           }
220*795d594fSAndroid Build Coastguard Worker         } else if (inst->IsSwitch()) {
221*795d594fSAndroid Build Coastguard Worker           // TODO: Iterate through all switch targets.
222*795d594fSAndroid Build Coastguard Worker           const uint16_t* insns = reinterpret_cast<const uint16_t*>(inst);
223*795d594fSAndroid Build Coastguard Worker           /* make sure the start of the switch is in range */
224*795d594fSAndroid Build Coastguard Worker           int32_t switch_offset = insns[1] | (static_cast<int32_t>(insns[2]) << 16);
225*795d594fSAndroid Build Coastguard Worker           /* offset to switch table is a relative branch-style offset */
226*795d594fSAndroid Build Coastguard Worker           const uint16_t* switch_insns = insns + switch_offset;
227*795d594fSAndroid Build Coastguard Worker           uint32_t switch_count = switch_insns[1];
228*795d594fSAndroid Build Coastguard Worker           int32_t targets_offset;
229*795d594fSAndroid Build Coastguard Worker           if ((*insns & 0xff) == Instruction::PACKED_SWITCH) {
230*795d594fSAndroid Build Coastguard Worker             /* 0=sig, 1=count, 2/3=firstKey */
231*795d594fSAndroid Build Coastguard Worker             targets_offset = 4;
232*795d594fSAndroid Build Coastguard Worker           } else {
233*795d594fSAndroid Build Coastguard Worker             /* 0=sig, 1=count, 2..count*2 = keys */
234*795d594fSAndroid Build Coastguard Worker             targets_offset = 2 + 2 * switch_count;
235*795d594fSAndroid Build Coastguard Worker           }
236*795d594fSAndroid Build Coastguard Worker           /* make sure the end of the switch is in range */
237*795d594fSAndroid Build Coastguard Worker           /* verify each switch target */
238*795d594fSAndroid Build Coastguard Worker           for (uint32_t targ = 0; targ < switch_count; targ++) {
239*795d594fSAndroid Build Coastguard Worker             int32_t offset =
240*795d594fSAndroid Build Coastguard Worker                 static_cast<int32_t>(switch_insns[targets_offset + targ * 2]) |
241*795d594fSAndroid Build Coastguard Worker                 static_cast<int32_t>(switch_insns[targets_offset + targ * 2 + 1] << 16);
242*795d594fSAndroid Build Coastguard Worker             int32_t abs_offset = dex_pc + offset;
243*795d594fSAndroid Build Coastguard Worker             auto target_it = dex_pc_to_node_id.find(abs_offset);
244*795d594fSAndroid Build Coastguard Worker             if (target_it != dex_pc_to_node_id.end()) {
245*795d594fSAndroid Build Coastguard Worker               // TODO: value label.
246*795d594fSAndroid Build Coastguard Worker               taken_edges << "  node" << last_node_id << ":p" << dex_pc
247*795d594fSAndroid Build Coastguard Worker                   << " -> node" << target_it->second << ":p" << (abs_offset)
248*795d594fSAndroid Build Coastguard Worker                   << ";\n";
249*795d594fSAndroid Build Coastguard Worker             }
250*795d594fSAndroid Build Coastguard Worker           }
251*795d594fSAndroid Build Coastguard Worker         }
252*795d594fSAndroid Build Coastguard Worker 
253*795d594fSAndroid Build Coastguard Worker         // Exception edges. If this is not the first instruction in the block
254*795d594fSAndroid Build Coastguard Worker         if (block_start_dex_pc != dex_pc) {
255*795d594fSAndroid Build Coastguard Worker           std::set<uint32_t> current_handler_pcs;
256*795d594fSAndroid Build Coastguard Worker           CatchHandlerIterator catch_it(accessor, dex_pc);
257*795d594fSAndroid Build Coastguard Worker           for (; catch_it.HasNext(); catch_it.Next()) {
258*795d594fSAndroid Build Coastguard Worker             current_handler_pcs.insert(catch_it.GetHandlerAddress());
259*795d594fSAndroid Build Coastguard Worker           }
260*795d594fSAndroid Build Coastguard Worker           if (current_handler_pcs != exception_targets) {
261*795d594fSAndroid Build Coastguard Worker             exception_targets.clear();  // Clear so we don't do something at the end.
262*795d594fSAndroid Build Coastguard Worker             blocks_with_detailed_exceptions.insert(block_start_dex_pc);
263*795d594fSAndroid Build Coastguard Worker           }
264*795d594fSAndroid Build Coastguard Worker         }
265*795d594fSAndroid Build Coastguard Worker 
266*795d594fSAndroid Build Coastguard Worker         if (inst->IsReturn() ||
267*795d594fSAndroid Build Coastguard Worker             (inst->Opcode() == Instruction::THROW) ||
268*795d594fSAndroid Build Coastguard Worker             (inst->IsBranch() && inst->IsUnconditional())) {
269*795d594fSAndroid Build Coastguard Worker           // No fall-through.
270*795d594fSAndroid Build Coastguard Worker           last_node_id = std::numeric_limits<uint32_t>::max();
271*795d594fSAndroid Build Coastguard Worker         }
272*795d594fSAndroid Build Coastguard Worker         old_dex_pc = pair.DexPc();
273*795d594fSAndroid Build Coastguard Worker       }
274*795d594fSAndroid Build Coastguard Worker       // Finish up the last block, if it had common exceptions.
275*795d594fSAndroid Build Coastguard Worker       if (!exception_targets.empty()) {
276*795d594fSAndroid Build Coastguard Worker         // It seems the last block had common exception handlers. Add the exception edges now.
277*795d594fSAndroid Build Coastguard Worker         uint32_t node_id = dex_pc_to_node_id.find(block_start_dex_pc)->second;
278*795d594fSAndroid Build Coastguard Worker         for (uint32_t handler_pc : exception_targets) {
279*795d594fSAndroid Build Coastguard Worker           auto node_id_it = dex_pc_to_incl_id.find(handler_pc);
280*795d594fSAndroid Build Coastguard Worker           if (node_id_it != dex_pc_to_incl_id.end()) {
281*795d594fSAndroid Build Coastguard Worker             exception_edges << "  node" << node_id
282*795d594fSAndroid Build Coastguard Worker                 << " -> node" << node_id_it->second << ":p" << handler_pc
283*795d594fSAndroid Build Coastguard Worker                 << ";\n";
284*795d594fSAndroid Build Coastguard Worker           }
285*795d594fSAndroid Build Coastguard Worker         }
286*795d594fSAndroid Build Coastguard Worker         exception_targets.clear();
287*795d594fSAndroid Build Coastguard Worker       }
288*795d594fSAndroid Build Coastguard Worker     }
289*795d594fSAndroid Build Coastguard Worker 
290*795d594fSAndroid Build Coastguard Worker     // Second pass for detailed exception blocks.
291*795d594fSAndroid Build Coastguard Worker     // TODO
292*795d594fSAndroid Build Coastguard Worker     // Exception edges. If this is not the first instruction in the block
293*795d594fSAndroid Build Coastguard Worker     for (uint32_t dex_pc : blocks_with_detailed_exceptions) {
294*795d594fSAndroid Build Coastguard Worker       const Instruction* inst = &accessor.InstructionAt(dex_pc);
295*795d594fSAndroid Build Coastguard Worker       uint32_t this_node_id = dex_pc_to_incl_id.find(dex_pc)->second;
296*795d594fSAndroid Build Coastguard Worker       while (true) {
297*795d594fSAndroid Build Coastguard Worker         CatchHandlerIterator catch_it(accessor, dex_pc);
298*795d594fSAndroid Build Coastguard Worker         if (catch_it.HasNext()) {
299*795d594fSAndroid Build Coastguard Worker           std::set<uint32_t> handled_targets;
300*795d594fSAndroid Build Coastguard Worker           for (; catch_it.HasNext(); catch_it.Next()) {
301*795d594fSAndroid Build Coastguard Worker             uint32_t handler_pc = catch_it.GetHandlerAddress();
302*795d594fSAndroid Build Coastguard Worker             auto it = handled_targets.find(handler_pc);
303*795d594fSAndroid Build Coastguard Worker             if (it == handled_targets.end()) {
304*795d594fSAndroid Build Coastguard Worker               auto node_id_it = dex_pc_to_incl_id.find(handler_pc);
305*795d594fSAndroid Build Coastguard Worker               if (node_id_it != dex_pc_to_incl_id.end()) {
306*795d594fSAndroid Build Coastguard Worker                 exception_edges << "  node" << this_node_id << ":p" << dex_pc
307*795d594fSAndroid Build Coastguard Worker                     << " -> node" << node_id_it->second << ":p" << handler_pc
308*795d594fSAndroid Build Coastguard Worker                     << ";\n";
309*795d594fSAndroid Build Coastguard Worker               }
310*795d594fSAndroid Build Coastguard Worker 
311*795d594fSAndroid Build Coastguard Worker               // Mark as done.
312*795d594fSAndroid Build Coastguard Worker               handled_targets.insert(handler_pc);
313*795d594fSAndroid Build Coastguard Worker             }
314*795d594fSAndroid Build Coastguard Worker           }
315*795d594fSAndroid Build Coastguard Worker         }
316*795d594fSAndroid Build Coastguard Worker         if (inst->IsBasicBlockEnd()) {
317*795d594fSAndroid Build Coastguard Worker           break;
318*795d594fSAndroid Build Coastguard Worker         }
319*795d594fSAndroid Build Coastguard Worker 
320*795d594fSAndroid Build Coastguard Worker         // Loop update. Have a break-out if the next instruction is a branch target and thus in
321*795d594fSAndroid Build Coastguard Worker         // another block.
322*795d594fSAndroid Build Coastguard Worker         dex_pc += inst->SizeInCodeUnits();
323*795d594fSAndroid Build Coastguard Worker         if (dex_pc >= accessor.InsnsSizeInCodeUnits()) {
324*795d594fSAndroid Build Coastguard Worker           break;
325*795d594fSAndroid Build Coastguard Worker         }
326*795d594fSAndroid Build Coastguard Worker         if (dex_pc_to_node_id.find(dex_pc) != dex_pc_to_node_id.end()) {
327*795d594fSAndroid Build Coastguard Worker           break;
328*795d594fSAndroid Build Coastguard Worker         }
329*795d594fSAndroid Build Coastguard Worker         inst = inst->Next();
330*795d594fSAndroid Build Coastguard Worker       }
331*795d594fSAndroid Build Coastguard Worker     }
332*795d594fSAndroid Build Coastguard Worker 
333*795d594fSAndroid Build Coastguard Worker     // Write out the sub-graphs to make edges styled.
334*795d594fSAndroid Build Coastguard Worker     os << "\n";
335*795d594fSAndroid Build Coastguard Worker     os << "  subgraph regular_edges {\n";
336*795d594fSAndroid Build Coastguard Worker     os << "    edge [color=\"#000000\",weight=.3,len=3];\n\n";
337*795d594fSAndroid Build Coastguard Worker     os << "    " << regular_edges.str() << "\n";
338*795d594fSAndroid Build Coastguard Worker     os << "  }\n\n";
339*795d594fSAndroid Build Coastguard Worker 
340*795d594fSAndroid Build Coastguard Worker     os << "  subgraph taken_edges {\n";
341*795d594fSAndroid Build Coastguard Worker     os << "    edge [color=\"#00FF00\",weight=.3,len=3];\n\n";
342*795d594fSAndroid Build Coastguard Worker     os << "    " << taken_edges.str() << "\n";
343*795d594fSAndroid Build Coastguard Worker     os << "  }\n\n";
344*795d594fSAndroid Build Coastguard Worker 
345*795d594fSAndroid Build Coastguard Worker     os << "  subgraph exception_edges {\n";
346*795d594fSAndroid Build Coastguard Worker     os << "    edge [color=\"#FF0000\",weight=.3,len=3];\n\n";
347*795d594fSAndroid Build Coastguard Worker     os << "    " << exception_edges.str() << "\n";
348*795d594fSAndroid Build Coastguard Worker     os << "  }\n\n";
349*795d594fSAndroid Build Coastguard Worker   }
350*795d594fSAndroid Build Coastguard Worker 
351*795d594fSAndroid Build Coastguard Worker   os << "}\n";
352*795d594fSAndroid Build Coastguard Worker }
353*795d594fSAndroid Build Coastguard Worker 
354*795d594fSAndroid Build Coastguard Worker 
355*795d594fSAndroid Build Coastguard Worker }  // namespace art
356