| /* | |
| * Copyright (C) 2017 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 "GraphDump.h" | |
| #include "HalInterfaces.h" | |
| #include <set> | |
| #include <iostream> | |
| namespace android { | |
| namespace nn { | |
| // Provide short name for OperandType value. | |
| static std::string translate(OperandType type) { | |
| switch (type) { | |
| case OperandType::FLOAT32: return "F32"; | |
| case OperandType::INT32: return "I32"; | |
| case OperandType::UINT32: return "U32"; | |
| case OperandType::TENSOR_FLOAT32: return "TF32"; | |
| case OperandType::TENSOR_INT32: return "TI32"; | |
| case OperandType::TENSOR_QUANT8_ASYMM: return "TQ8A"; | |
| case OperandType::OEM: return "OEM"; | |
| case OperandType::TENSOR_OEM_BYTE: return "TOEMB"; | |
| default: return toString(type); | |
| } | |
| } | |
| void graphDump(const char* name, const Model& model, std::ostream& outStream) { | |
| // Operand nodes are named "d" (operanD) followed by operand index. | |
| // Operation nodes are named "n" (operatioN) followed by operation index. | |
| // (These names are not the names that are actually displayed -- those | |
| // names are given by the "label" attribute.) | |
| outStream << "// " << name << std::endl; | |
| outStream << "digraph {" << std::endl; | |
| // model inputs and outputs | |
| std::set<uint32_t> modelIO; | |
| for (unsigned i = 0, e = model.inputIndexes.size(); i < e; i++) { | |
| modelIO.insert(model.inputIndexes[i]); | |
| } | |
| for (unsigned i = 0, e = model.outputIndexes.size(); i < e; i++) { | |
| modelIO.insert(model.outputIndexes[i]); | |
| } | |
| // model operands | |
| for (unsigned i = 0, e = model.operands.size(); i < e; i++) { | |
| outStream << " d" << i << " ["; | |
| if (modelIO.count(i)) { | |
| outStream << "style=filled fillcolor=black fontcolor=white "; | |
| } | |
| outStream << "label=\"" << i; | |
| const Operand& opnd = model.operands[i]; | |
| const char* kind = nullptr; | |
| switch (opnd.lifetime) { | |
| case OperandLifeTime::CONSTANT_COPY: | |
| kind = "COPY"; | |
| break; | |
| case OperandLifeTime::CONSTANT_REFERENCE: | |
| kind = "REF"; | |
| break; | |
| case OperandLifeTime::NO_VALUE: | |
| kind = "NO"; | |
| break; | |
| default: | |
| // nothing interesting | |
| break; | |
| } | |
| if (kind) { | |
| outStream << ": " << kind; | |
| } | |
| outStream << "\\n" << translate(opnd.type); | |
| if (opnd.dimensions.size()) { | |
| outStream << "("; | |
| for (unsigned i = 0, e = opnd.dimensions.size(); i < e; i++) { | |
| if (i > 0) { | |
| outStream << "x"; | |
| } | |
| outStream << opnd.dimensions[i]; | |
| } | |
| outStream << ")"; | |
| } | |
| outStream << "\"]" << std::endl; | |
| } | |
| // model operations | |
| for (unsigned i = 0, e = model.operations.size(); i < e; i++) { | |
| const Operation& operation = model.operations[i]; | |
| outStream << " n" << i << " [shape=box"; | |
| const uint32_t maxArity = std::max(operation.inputs.size(), operation.outputs.size()); | |
| if (maxArity > 1) { | |
| if (maxArity == operation.inputs.size()) { | |
| outStream << " ordering=in"; | |
| } else { | |
| outStream << " ordering=out"; | |
| } | |
| } | |
| outStream << " label=\"" << i << ": " | |
| << toString(operation.type) << "\"]" << std::endl; | |
| { | |
| // operation inputs | |
| for (unsigned in = 0, inE = operation.inputs.size(); in < inE; in++) { | |
| outStream << " d" << operation.inputs[in] << " -> n" << i; | |
| if (inE > 1) { | |
| outStream << " [label=" << in << "]"; | |
| } | |
| outStream << std::endl; | |
| } | |
| } | |
| { | |
| // operation outputs | |
| for (unsigned out = 0, outE = operation.outputs.size(); out < outE; out++) { | |
| outStream << " n" << i << " -> d" << operation.outputs[out]; | |
| if (outE > 1) { | |
| outStream << " [label=" << out << "]"; | |
| } | |
| outStream << std::endl; | |
| } | |
| } | |
| } | |
| outStream << "}" << std::endl; | |
| } | |
| } // namespace nn | |
| } // namespace android |