2 * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 #include <circledump/Dump.h>
20 #include "OpPrinter.h"
24 #include <algorithm> // min
25 #include <iomanip> // setfill
30 void dump_buffer(std::ostream &os, const uint8_t *buffer, size_t size, size_t amount)
32 std::ios_base::fmtflags saveflags(os.flags());
35 bool ellipsis = amount > 0 && size > 4;
36 size_t count = ellipsis ? std::min(size, amount) : size;
38 for (size_t i = 0; i < count; i++)
45 os << std::showbase << std::setfill('0') << std::setw(2);
46 os << std::hex << (uint32_t)buffer[i];
58 void dump_vector(std::ostream &os, const std::vector<int32_t> &vs)
70 std::ostream &operator<<(std::ostream &os, const std::vector<int32_t> &vect)
72 circledump::dump_vector(os, vect);
76 template <typename T> void dump_fbvect(std::ostream &os, const flatbuffers::Vector<T> *fbvect)
78 if (fbvect == nullptr)
81 bool ellipsis = (fbvect->size() > 4);
82 auto limit_size = ellipsis ? 4 : fbvect->size();
86 os << "(" << fbvect->size() << ") ";
88 for (uint32_t q = 0; q < limit_size; q++)
100 template <typename T>
101 std::ostream &operator<<(std::ostream &os, const flatbuffers::Vector<T> *fbvect)
103 dump_fbvect(os, fbvect);
107 void dump_sub_graph(std::ostream &os, circleread::Reader &reader)
109 auto tensors = reader.tensors();
110 auto operators = reader.operators();
111 auto data_format = reader.data_format();
114 os << "Data Format:" << std::endl;
115 if (data_format == circle::DataFormat::DataFormat_CHANNELS_LAST)
117 os << "CHANNEL_LAST (NHWC for 2d, NDHWC for 3d data)" << std::endl;
119 else if (data_format == circle::DataFormat::DataFormat_CHANNELS_FIRST)
121 os << "CHANNEL_FIRST (NCHW for 2d, NCDHW for 3d data)" << std::endl;
125 // dump operands(tensors)
126 os << "Operands: T(subgraph index : tensor index) TYPE (shape) (shape_signature) "
127 << "B(buffer index) OperandName" << std::endl;
128 for (uint32_t i = 0; i < tensors->Length(); ++i)
130 // TODO refactor to some better structure
131 auto tensor = tensors->Get(i);
132 std::vector<int32_t> dims = {-1};
135 dims = circleread::as_index_vector(tensor->shape());
137 os << "T(" << reader.subgraph_index() << ":" << i << ") " << circleread::tensor_type(tensor)
139 os << "(" << dims << ") ";
140 if (tensor->shape_signature())
142 std::vector<int32_t> dims_sig = circleread::as_index_vector(tensor->shape_signature());
143 os << "(" << dims_sig << ") ";
145 os << "B(" << tensor->buffer() << ") ";
146 os << circleread::tensor_name(tensor) << std::endl;
148 if (auto q_params = tensor->quantization())
150 if ((q_params->min() && q_params->max()) || (q_params->scale() && q_params->zero_point()))
152 std::string strquantiz = " Quantization: ";
153 std::string strqindent(strquantiz.size(), ' ');
158 os << "min(" << q_params->min() << ") ";
159 if (q_params->min()->size() > 1)
160 os << std::endl << strqindent;
164 os << "max(" << q_params->max() << ") ";
165 if (q_params->max()->size() > 1)
166 os << std::endl << strqindent;
168 if (q_params->scale())
170 os << "scale(" << q_params->scale() << ") ";
171 if (q_params->scale()->size() > 1)
172 os << std::endl << strqindent;
174 if (q_params->zero_point())
176 os << "zeropt(" << q_params->zero_point() << ") ";
177 if (q_params->zero_point()->size() > 1)
178 os << std::endl << strqindent;
180 os << "quantized_dimension(" << q_params->quantized_dimension() << ")";
189 os << "Operators: O(subgraph index : operator index) OpCodeName " << std::endl;
190 os << " Option(values) ... <-- depending on OpCode" << std::endl;
191 os << " I T(tensor index) OperandName <-- as input" << std::endl;
192 os << " O T(tensor index) OperandName <-- as output" << std::endl;
193 for (uint32_t i = 0; i < operators->Length(); ++i)
195 const auto op = operators->Get(i);
196 circle::BuiltinOperator builtincode = reader.builtin_code(op);
198 const std::vector<int32_t> &inputs = circleread::as_index_vector(op->inputs());
199 const std::vector<int32_t> &outputs = circleread::as_index_vector(op->outputs());
200 auto op_name = reader.opcode_name(op);
202 os << "O(" << reader.subgraph_index() << ":" << i << ") " << op_name << " ";
205 if (auto op_prn = OpPrinterRegistry::get().lookup(builtincode))
207 op_prn->options(op, os);
210 for (auto input : inputs)
212 os << " I T(" << reader.subgraph_index() << ":" << input << ") ";
215 auto tensor = tensors->Get(input);
216 os << circleread::tensor_name(tensor);
220 for (auto output : outputs)
222 os << " O T(" << reader.subgraph_index() << ":" << output << ") ";
225 auto tensor = tensors->Get(output);
226 os << circleread::tensor_name(tensor);
233 // dump network inputs/outputs
234 os << "Inputs/Outputs: I(input)/O(output) T(tensor index) OperandName" << std::endl;
236 for (const auto input : reader.inputs())
238 auto tensor = tensors->Get(input);
239 std::string name = circleread::tensor_name(tensor);
240 os << "I T(" << reader.subgraph_index() << ":" << input << ") " << name << std::endl;
243 for (const auto output : reader.outputs())
245 auto tensor = tensors->Get(output);
246 std::string name = circleread::tensor_name(tensor);
247 os << "O T(" << reader.subgraph_index() << ":" << output << ") " << name << std::endl;
253 void dump_model(std::ostream &os, const circle::Model *model)
255 circleread::Reader reader(model);
257 uint32_t num_subgraph = reader.num_subgraph();
259 // dump model version
260 os << "===================================================================" << std::endl;
261 os << "Model version: " << reader.version() << std::endl;
262 os << " # sub graphs: " << num_subgraph << std::endl;
265 auto opcodes = reader.opcodes();
266 auto buffers = reader.buffers();
268 // dump operator_codes
269 os << "Operator Codes: [order] OpCodeName (OpCode Enum)" << std::endl;
270 int32_t opcode_index = 0;
271 for (auto opcode : opcodes)
273 circle::BuiltinOperator op_code = opcode->builtin_code();
274 auto op_name = circleread::opcode_name(opcode);
275 auto op_version = opcode->version();
277 os << "[" << opcode_index << "] " << op_name << " (code: " << op_code
278 << ", version: " << op_version << ")" << std::endl;
285 os << "Buffers: B(index) (length) values, if any" << std::endl;
286 for (uint32_t i = 0; i < buffers->Length(); ++i)
288 const uint8_t *buff_data;
289 size_t size = reader.buffer_info(i, &buff_data);
291 os << "B(" << i << ") (" << size << ") ";
292 if (buff_data != nullptr)
294 dump_buffer(os, buff_data, size, 16);
300 for (uint32_t sg = 0; sg < num_subgraph; ++sg)
302 reader.select_subgraph(sg);
304 os << "-------------------------------------------------------------------" << std::endl;
305 os << "Sub-Graph: #" << sg << " " << reader.subgraph_name() << std::endl;
308 dump_sub_graph(os, reader);
311 os << "===================================================================" << std::endl;
314 } // namespace circledump
316 std::ostream &operator<<(std::ostream &os, const circle::Model *model)
318 circledump::dump_model(os, model);