Imported Upstream version 1.7.0
[platform/core/ml/nnfw.git] / compiler / circledump / src / Dump.cpp
1 /*
2  * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
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 <circledump/Dump.h>
18
19 #include "Read.h"
20 #include "OpPrinter.h"
21
22 #include <ostream>
23
24 #include <algorithm> // min
25 #include <iomanip>   // setfill
26
27 namespace circledump
28 {
29
30 void dump_buffer(std::ostream &os, const uint8_t *buffer, size_t size, size_t amount)
31 {
32   std::ios_base::fmtflags saveflags(os.flags());
33
34   bool second = false;
35   bool ellipsis = amount > 0 && size > 4;
36   size_t count = ellipsis ? std::min(size, amount) : size;
37
38   for (size_t i = 0; i < count; i++)
39   {
40     if (second)
41     {
42       os << " ";
43     }
44
45     os << std::showbase << std::setfill('0') << std::setw(2);
46     os << std::hex << (uint32_t)buffer[i];
47
48     second = true;
49   }
50   if (ellipsis)
51   {
52     os << " ...";
53   }
54
55   os.flags(saveflags);
56 }
57
58 void dump_vector(std::ostream &os, const std::vector<int32_t> &vs)
59 {
60   uint32_t seq = 0;
61   for (auto &v : vs)
62   {
63     if (seq)
64       os << ", ";
65     os << v;
66     seq++;
67   }
68 }
69
70 std::ostream &operator<<(std::ostream &os, const std::vector<int32_t> &vect)
71 {
72   circledump::dump_vector(os, vect);
73   return os;
74 }
75
76 template <typename T> void dump_fbvect(std::ostream &os, const flatbuffers::Vector<T> *fbvect)
77 {
78   if (fbvect == nullptr)
79     return;
80
81   bool ellipsis = (fbvect->size() > 4);
82   auto limit_size = ellipsis ? 4 : fbvect->size();
83
84   if (ellipsis)
85   {
86     os << "(" << fbvect->size() << ") ";
87   }
88   for (uint32_t q = 0; q < limit_size; q++)
89   {
90     if (q)
91       os << ", ";
92     os << fbvect->Get(q);
93   }
94   if (ellipsis)
95   {
96     os << " ... ";
97   }
98 }
99
100 template <typename T>
101 std::ostream &operator<<(std::ostream &os, const flatbuffers::Vector<T> *fbvect)
102 {
103   dump_fbvect(os, fbvect);
104   return os;
105 }
106
107 void dump_sub_graph(std::ostream &os, circleread::Reader &reader)
108 {
109   auto tensors = reader.tensors();
110   auto operators = reader.operators();
111   auto data_format = reader.data_format();
112
113   // dump data_format
114   os << "Data Format:" << std::endl;
115   if (data_format == circle::DataFormat::DataFormat_CHANNELS_LAST)
116   {
117     os << "CHANNEL_LAST (NHWC for 2d, NDHWC for 3d data)" << std::endl;
118   }
119   else if (data_format == circle::DataFormat::DataFormat_CHANNELS_FIRST)
120   {
121     os << "CHANNEL_FIRST (NCHW for 2d, NCDHW for 3d data)" << std::endl;
122   }
123   os << std::endl;
124
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)
129   {
130     // TODO refactor to some better structure
131     auto tensor = tensors->Get(i);
132     std::vector<int32_t> dims = {-1};
133
134     if (tensor->shape())
135       dims = circleread::as_index_vector(tensor->shape());
136
137     os << "T(" << reader.subgraph_index() << ":" << i << ") " << circleread::tensor_type(tensor)
138        << " ";
139     os << "(" << dims << ") ";
140     if (tensor->shape_signature())
141     {
142       std::vector<int32_t> dims_sig = circleread::as_index_vector(tensor->shape_signature());
143       os << "(" << dims_sig << ") ";
144     }
145     os << "B(" << tensor->buffer() << ") ";
146     os << circleread::tensor_name(tensor) << std::endl;
147
148     if (auto q_params = tensor->quantization())
149     {
150       if ((q_params->min() && q_params->max()) || (q_params->scale() && q_params->zero_point()))
151       {
152         std::string strquantiz = "    Quantization: ";
153         std::string strqindent(strquantiz.size(), ' ');
154         os << strquantiz;
155
156         if (q_params->min())
157         {
158           os << "min(" << q_params->min() << ") ";
159           if (q_params->min()->size() > 1)
160             os << std::endl << strqindent;
161         }
162         if (q_params->max())
163         {
164           os << "max(" << q_params->max() << ") ";
165           if (q_params->max()->size() > 1)
166             os << std::endl << strqindent;
167         }
168         if (q_params->scale())
169         {
170           os << "scale(" << q_params->scale() << ") ";
171           if (q_params->scale()->size() > 1)
172             os << std::endl << strqindent;
173         }
174         if (q_params->zero_point())
175         {
176           os << "zeropt(" << q_params->zero_point() << ") ";
177           if (q_params->zero_point()->size() > 1)
178             os << std::endl << strqindent;
179         }
180         os << "quantized_dimension(" << q_params->quantized_dimension() << ")";
181
182         os << std::endl;
183       }
184     }
185   }
186   os << std::endl;
187
188   // dump operators
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)
194   {
195     const auto op = operators->Get(i);
196     circle::BuiltinOperator builtincode = reader.builtin_code(op);
197
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);
201
202     os << "O(" << reader.subgraph_index() << ":" << i << ") " << op_name << " ";
203     os << std::endl;
204
205     if (auto op_prn = OpPrinterRegistry::get().lookup(builtincode))
206     {
207       op_prn->options(op, os);
208     }
209
210     for (auto input : inputs)
211     {
212       os << "    I T(" << reader.subgraph_index() << ":" << input << ") ";
213       if (input >= 0)
214       {
215         auto tensor = tensors->Get(input);
216         os << circleread::tensor_name(tensor);
217       }
218       os << std::endl;
219     }
220     for (auto output : outputs)
221     {
222       os << "    O T(" << reader.subgraph_index() << ":" << output << ") ";
223       if (output >= 0)
224       {
225         auto tensor = tensors->Get(output);
226         os << circleread::tensor_name(tensor);
227       }
228       os << std::endl;
229     }
230   }
231   os << std::endl;
232
233   // dump network inputs/outputs
234   os << "Inputs/Outputs: I(input)/O(output) T(tensor index) OperandName" << std::endl;
235
236   for (const auto input : reader.inputs())
237   {
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;
241   }
242
243   for (const auto output : reader.outputs())
244   {
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;
248   }
249
250   os << std::endl;
251 }
252
253 void dump_model(std::ostream &os, const circle::Model *model)
254 {
255   circleread::Reader reader(model);
256
257   uint32_t num_subgraph = reader.num_subgraph();
258
259   // dump model version
260   os << "===================================================================" << std::endl;
261   os << "Model version: " << reader.version() << std::endl;
262   os << " # sub graphs: " << num_subgraph << std::endl;
263   os << std::endl;
264
265   auto opcodes = reader.opcodes();
266   auto buffers = reader.buffers();
267
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)
272   {
273     circle::BuiltinOperator op_code = opcode->builtin_code();
274     auto op_name = circleread::opcode_name(opcode);
275     auto op_version = opcode->version();
276
277     os << "[" << opcode_index << "] " << op_name << " (code: " << op_code
278        << ", version: " << op_version << ")" << std::endl;
279
280     opcode_index++;
281   }
282   os << std::endl;
283
284   // dump buffer
285   os << "Buffers: B(index) (length) values, if any" << std::endl;
286   for (uint32_t i = 0; i < buffers->Length(); ++i)
287   {
288     const uint8_t *buff_data;
289     size_t size = reader.buffer_info(i, &buff_data);
290
291     os << "B(" << i << ") (" << size << ") ";
292     if (buff_data != nullptr)
293     {
294       dump_buffer(os, buff_data, size, 16);
295     }
296     os << std::endl;
297   }
298   os << std::endl;
299
300   for (uint32_t sg = 0; sg < num_subgraph; ++sg)
301   {
302     reader.select_subgraph(sg);
303
304     os << "-------------------------------------------------------------------" << std::endl;
305     os << "Sub-Graph: #" << sg << " " << reader.subgraph_name() << std::endl;
306     os << std::endl;
307
308     dump_sub_graph(os, reader);
309   }
310
311   os << "===================================================================" << std::endl;
312 }
313
314 } // namespace circledump
315
316 std::ostream &operator<<(std::ostream &os, const circle::Model *model)
317 {
318   circledump::dump_model(os, model);
319   return os;
320 }