eb0b743d3dfb1446c0d6574bc856bd27ceaed9cf
[platform/core/ml/nnfw.git] / runtime / onert / api / src / nnfw_api_internal.cc
1 /*
2  * Copyright (c) 2019 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 "nnfw_api_internal.h"
18 #include "CustomKernelRegistry.h"
19 #include "compiler/Compiler.h"
20 #include "util/ConfigSource.h"
21 #include "exec/Execution.h"
22 #include "circle_loader.h"
23 #include "tflite_loader.h"
24 #include "json/json.h"
25 #include "ir/OpCode.h"
26 #include <fstream>
27 #include <iostream>
28 #include <string>
29 #include <vector>
30 #include <dirent.h>
31 #include <util/ConfigSource.h>
32 #include <misc/string_helpers.h>
33
34 /*
35  * API does not accept string argument longer than max length below
36  */
37 #define MAX_BACKEND_NAME_LENGTH 32
38 #define MAX_OP_NAME_LENGTH 64
39 #define MAX_PATH_LENGTH 1024
40
41 // Is null-terminating in length ?
42 static bool null_terminating(const char *str, uint32_t length)
43 {
44   for (uint32_t i = 0; i < length; i++)
45   {
46     if (*(str + i) == '\0')
47     {
48       return true;
49     }
50   }
51   return false;
52 }
53
54 static onert::ir::Layout convertLayout(NNFW_LAYOUT layout)
55 {
56   if (layout == NNFW_LAYOUT_CHANNELS_LAST)
57   {
58     return onert::ir::Layout::NHWC;
59   }
60   else if (layout == NNFW_LAYOUT_CHANNELS_FIRST)
61   {
62     return onert::ir::Layout::NCHW;
63   }
64   return onert::ir::Layout::UNKNOWN;
65 }
66
67 nnfw_session::nnfw_session()
68     : _subgraphs{nullptr}, _execution{nullptr},
69       _kernel_registry{std::make_shared<onert::frontend::custom::KernelRegistry>()}
70 {
71   // DO NOTHING
72 }
73
74 nnfw_session::~nnfw_session() = default;
75
76 NNFW_STATUS nnfw_session::load_circle_from_buffer(uint8_t *buffer, size_t size)
77 {
78   if (!isStateInitialized())
79     return NNFW_STATUS_INVALID_STATE;
80
81   if (!buffer)
82     return NNFW_STATUS_UNEXPECTED_NULL;
83
84   if (size == 0)
85     return NNFW_STATUS_ERROR;
86
87   _subgraphs = onert::circle_loader::loadModel(buffer, size);
88   _compiler = std::make_unique<onert::compiler::Compiler>(_subgraphs);
89
90   _state = State::MODEL_LOADED;
91   return NNFW_STATUS_NO_ERROR;
92 }
93
94 NNFW_STATUS nnfw_session::load_model_from_file(const char *package_dir)
95 {
96   if (!isStateInitialized())
97     return NNFW_STATUS_INVALID_STATE;
98
99   if (!package_dir)
100   {
101     std::cerr << "package_dir is null." << std::endl;
102     return NNFW_STATUS_UNEXPECTED_NULL;
103   }
104
105   if (!null_terminating(package_dir, MAX_PATH_LENGTH))
106   {
107     std::cerr << "nnpackage path is too long" << std::endl;
108     return NNFW_STATUS_ERROR;
109   }
110
111   // TODO : add support for zipped package file load
112   DIR *dir;
113   if (!(dir = opendir(package_dir)))
114   {
115     std::cerr << "invalid nnpackge directory: " << package_dir << std::endl;
116     return NNFW_STATUS_ERROR;
117   }
118   closedir(dir);
119
120   try
121   {
122     std::string manifest_file_name(package_dir);
123     manifest_file_name += "/metadata/MANIFEST";
124     std::ifstream mfs(manifest_file_name);
125
126     // extract the filename of the first(index 0) model
127     // e.g. In MANIFEST file, { "models" : [ "firstmodel.tflite", "2nd.tflite" ] }
128     Json::Value root;
129     mfs >> root;
130     const Json::Value &models = root["models"];
131     const Json::Value &model_types = root["model-types"];
132
133     auto model_file_path = package_dir + std::string("/") + models[0].asString(); // first model
134     auto model_type = model_types[0].asString(); // first model's type
135     if (model_type == "tflite")
136     {
137       _subgraphs = onert::tflite_loader::loadModel(model_file_path.c_str());
138     }
139     else if (model_type == "circle")
140     {
141       _subgraphs = onert::circle_loader::loadModel(model_file_path.c_str());
142     }
143     else
144     {
145       std::cerr << "Unsupported model type in MANIFEST" << std::endl;
146       return NNFW_STATUS_ERROR;
147     }
148     _subgraphs->primary()->bindKernelBuilder(_kernel_registry->getBuilder());
149   }
150   catch (const std::exception &e)
151   {
152     std::cerr << "Error during model loading : " << e.what() << std::endl;
153     return NNFW_STATUS_ERROR;
154   }
155
156   _compiler = std::make_unique<onert::compiler::Compiler>(_subgraphs);
157
158   _state = State::MODEL_LOADED;
159   return NNFW_STATUS_NO_ERROR;
160 }
161
162 NNFW_STATUS nnfw_session::prepare()
163 {
164   // NOTE. If users want to run prepare() more than one time, this could be removed.
165   if (!isStateModelLoaded())
166   {
167     std::cerr << "Error during model prepare : ";
168     if (isStateInitialized())
169     {
170       std::cerr << "prepare should be run once";
171     }
172     else
173     {
174       std::cerr << "invalid state";
175     }
176     std::cerr << std::endl;
177     return NNFW_STATUS_INVALID_STATE;
178   }
179
180   if (!_subgraphs || !primary_subgraph() || primary_subgraph()->isBuildingPhase())
181   {
182     std::cerr << "Error during model prepare : "
183               << "prepare should be run after load_model" << std::endl;
184     return NNFW_STATUS_ERROR;
185   }
186
187   try
188   {
189     _subgraphs.reset();
190     std::shared_ptr<onert::exec::ExecutorMap> executors = _compiler->compile();
191     _execution = std::make_shared<onert::exec::Execution>(executors);
192   }
193   catch (const std::exception &e)
194   {
195     std::cerr << "Error during model prepare : " << e.what() << std::endl;
196     return NNFW_STATUS_ERROR;
197   }
198
199   _state = State::PREPARED;
200   return NNFW_STATUS_NO_ERROR;
201 }
202
203 NNFW_STATUS nnfw_session::run()
204 {
205   if (!isStatePreparedOrFinishedRun())
206   {
207     std::cerr << "Error during nnfw_session::run : "
208               << "run should be run after prepare" << std::endl;
209     return NNFW_STATUS_INVALID_STATE;
210   }
211
212   try
213   {
214     _execution->execute();
215   }
216   catch (const std::exception &e)
217   {
218     std::cerr << "Error during nnfw_session::run : " << e.what() << std::endl;
219     return NNFW_STATUS_ERROR;
220   }
221
222   _state = State::FINISHED_RUN;
223   return NNFW_STATUS_NO_ERROR;
224 }
225
226 NNFW_STATUS nnfw_session::run_async()
227 {
228   if (!isStatePreparedOrFinishedRun())
229   {
230     std::cerr << "Error during nnfw_session::run_async : "
231               << "run_async should be run after prepare" << std::endl;
232     return NNFW_STATUS_INVALID_STATE;
233   }
234
235   _execution->startExecute();
236
237   _state = State::RUNNING;
238   return NNFW_STATUS_NO_ERROR;
239 }
240
241 NNFW_STATUS nnfw_session::await()
242 {
243   if (!isStateRunning())
244   {
245     std::cerr << "Error during nnfw_session::run_await : "
246               << "run_await should be run after run_async" << std::endl;
247     return NNFW_STATUS_ERROR;
248   }
249
250   _execution->waitFinish();
251
252   _state = State::FINISHED_RUN;
253   return NNFW_STATUS_NO_ERROR;
254 }
255
256 NNFW_STATUS nnfw_session::set_input(uint32_t index, NNFW_TYPE /*type*/, const void *buffer,
257                                     size_t length)
258 {
259   if (!isStatePreparedOrFinishedRun())
260   {
261     std::cerr << "Error during nnfw_session::set_input : invalid state" << std::endl;
262     return NNFW_STATUS_INVALID_STATE;
263   }
264
265   if (!buffer && length != 0)
266   {
267     std::cerr
268         << "Error during nnfw_session::set_input : given buffer is NULL but the length is not 0"
269         << std::endl;
270     return NNFW_STATUS_ERROR;
271   }
272
273   try
274   {
275     _execution->setInput(onert::ir::IOIndex(index), buffer, length);
276   }
277   catch (const std::exception &e)
278   {
279     std::cerr << "Error during nnfw_session::set_input : " << e.what() << std::endl;
280     return NNFW_STATUS_ERROR;
281   }
282   return NNFW_STATUS_NO_ERROR;
283 }
284
285 NNFW_STATUS nnfw_session::set_output(uint32_t index, NNFW_TYPE /*type*/, void *buffer,
286                                      size_t length)
287 {
288   if (!isStatePreparedOrFinishedRun())
289   {
290     std::cerr << "Error during nnfw_session::set_output : invalid state" << std::endl;
291     return NNFW_STATUS_INVALID_STATE;
292   }
293
294   if (!buffer && length != 0)
295   {
296     std::cerr
297         << "Error during nnfw_session::set_output : given buffer is NULL but the length is not 0"
298         << std::endl;
299     return NNFW_STATUS_ERROR;
300   }
301
302   try
303   {
304     _execution->setOutput(onert::ir::IOIndex(index), buffer, length);
305   }
306   catch (const std::exception &e)
307   {
308     std::cerr << "Error during nnfw_session::set_output : " << e.what() << std::endl;
309     return NNFW_STATUS_ERROR;
310   }
311   return NNFW_STATUS_NO_ERROR;
312 }
313
314 NNFW_STATUS nnfw_session::input_size(uint32_t *number)
315 {
316   if (isStateInitialized()) // Model is not loaded
317     return NNFW_STATUS_INVALID_STATE;
318
319   try
320   {
321     if (number == nullptr)
322     {
323       std::cerr << "Error during nnfw_session::input_size, number is null pointer." << std::endl;
324       return NNFW_STATUS_UNEXPECTED_NULL;
325     }
326     *number = primary_subgraph()->getInputs().size();
327   }
328   catch (const std::exception &e)
329   {
330     std::cerr << "Error during nnfw_session::input_size : " << e.what() << std::endl;
331     return NNFW_STATUS_ERROR;
332   }
333   return NNFW_STATUS_NO_ERROR;
334 }
335
336 NNFW_STATUS nnfw_session::output_size(uint32_t *number)
337 {
338   if (isStateInitialized()) // Model is not loaded
339     return NNFW_STATUS_INVALID_STATE;
340
341   try
342   {
343     if (number == nullptr)
344     {
345       std::cerr << "Error during nnfw_session::output_size, number is null pointer." << std::endl;
346       return NNFW_STATUS_UNEXPECTED_NULL;
347     }
348     *number = primary_subgraph()->getOutputs().size();
349   }
350   catch (const std::exception &e)
351   {
352     std::cerr << "Error during nnfw_session::output_size" << e.what() << std::endl;
353     return NNFW_STATUS_ERROR;
354   }
355   return NNFW_STATUS_NO_ERROR;
356 }
357
358 NNFW_STATUS nnfw_session::set_input_layout(uint32_t index, NNFW_LAYOUT layout)
359 {
360   try
361   {
362     if (layout != NNFW_LAYOUT_NONE && layout != NNFW_LAYOUT_CHANNELS_FIRST &&
363         layout != NNFW_LAYOUT_CHANNELS_LAST)
364     {
365       std::cerr << "Error during nnfw_session::set_input_layout, not supported layout" << std::endl;
366       return NNFW_STATUS_ERROR;
367     }
368     _execution->setInputLayout(onert::ir::IOIndex(index), convertLayout(layout));
369   }
370   catch (const std::exception &e)
371   {
372     std::cerr << "Error during nnfw_session::set_input_layout : " << e.what() << std::endl;
373     return NNFW_STATUS_ERROR;
374   }
375   return NNFW_STATUS_NO_ERROR;
376 }
377
378 NNFW_STATUS nnfw_session::set_output_layout(uint32_t index, NNFW_LAYOUT layout)
379 {
380   try
381   {
382     if (layout != NNFW_LAYOUT_NONE && layout != NNFW_LAYOUT_CHANNELS_FIRST &&
383         layout != NNFW_LAYOUT_CHANNELS_LAST)
384     {
385       std::cerr << "Error during nnfw_session::set_output_layout, not supported layout"
386                 << std::endl;
387       return NNFW_STATUS_ERROR;
388     }
389     _execution->setOutputLayout(onert::ir::IOIndex(index), convertLayout(layout));
390   }
391   catch (const std::exception &e)
392   {
393     std::cerr << "Error during nnfw_session::set_output_layout : " << e.what() << std::endl;
394     return NNFW_STATUS_ERROR;
395   }
396   return NNFW_STATUS_NO_ERROR;
397 }
398
399 static NNFW_TYPE datatype_to_nnfw_dtype(onert::ir::DataType dt)
400 {
401   using onert::ir::DataType;
402   switch (dt)
403   {
404     case DataType::FLOAT32:
405       return NNFW_TYPE_TENSOR_FLOAT32;
406     case DataType::INT32:
407       return NNFW_TYPE_TENSOR_INT32;
408     case DataType::QUANT_UINT8_ASYMM:
409       return NNFW_TYPE_TENSOR_QUANT8_ASYMM;
410     case DataType::BOOL8:
411       return NNFW_TYPE_TENSOR_BOOL;
412     case DataType::UINT8:
413       return NNFW_TYPE_TENSOR_UINT8;
414     case DataType::INT64:
415       return NNFW_TYPE_TENSOR_INT64;
416     case DataType::UINT32:
417     case DataType::QUANT_INT8_SYMM:
418     default:
419       throw std::runtime_error("Error: Model has type that runtime API does not support.");
420   }
421 }
422
423 NNFW_STATUS nnfw_session::apply_tensorinfo(uint32_t index, nnfw_tensorinfo ti)
424 {
425   // sanity check
426   {
427     if (isStateInitialized())
428     {
429       std::cerr << "Error during set_input_tensorinfo : should be run after load_model"
430                 << std::endl;
431       return NNFW_STATUS_INVALID_STATE;
432     }
433
434     if (ti.rank <= 0 || ti.rank > NNFW_MAX_RANK)
435     {
436       std::cerr << "unsupported rank: " << ti.rank << std::endl;
437       return NNFW_STATUS_ERROR;
438     }
439
440     for (int32_t i = 0; i < ti.rank; ++i)
441     {
442       if (ti.dims[i] <= 0)
443       {
444         std::cerr << "dim must be positive integer but was " << ti.dims[i] << std::endl;
445         return NNFW_STATUS_ERROR;
446       }
447     }
448   }
449
450   if (!isStatePreparedOrFinishedRun())
451   {
452     // In this case, if we apply input shape in primary_subgraph, it will propagate after
453     // compilation and excution
454     auto ind = primary_subgraph()->getInputs().at(index);
455     auto &input = primary_subgraph()->operands().at(ind);
456
457     onert::ir::Shape new_shape(ti.rank);
458     for (int32_t i = 0; i < ti.rank; i++)
459       new_shape.dim(i) = ti.dims[i];
460
461     // overwrite input shape with the shape from ti
462     input.info().shape(new_shape);
463   }
464   else // when called after nnfw_session::prepare()
465   {
466     onert::ir::Shape new_shape(ti.rank);
467     for (int32_t i = 0; i < ti.rank; i++)
468       new_shape.dim(i) = ti.dims[i];
469
470     _execution->changeInputShape(onert::ir::IOIndex(index), new_shape);
471   }
472
473   return NNFW_STATUS_NO_ERROR;
474 }
475
476 NNFW_STATUS nnfw_session::set_input_tensorinfo(uint32_t index, const nnfw_tensorinfo *ti)
477 {
478   nnfw_tensorinfo ti_copy = *ti;
479   return apply_tensorinfo(index, ti_copy);
480 }
481
482 NNFW_STATUS nnfw_session::input_tensorinfo(uint32_t index, nnfw_tensorinfo *ti)
483 {
484   if (isStateInitialized())
485     return NNFW_STATUS_INVALID_STATE;
486
487   try
488   {
489     if (ti == nullptr)
490     {
491       std::cerr << "Error during nnfw_session::input_tensorinfo, tensorinfo is null pointer."
492                 << std::endl;
493       return NNFW_STATUS_UNEXPECTED_NULL;
494     }
495     if (index >= primary_subgraph()->getInputs().size())
496     {
497       std::cerr << "Error during nnfw_session::input_tensorinfo, index is out of range."
498                 << std::endl;
499       return NNFW_STATUS_ERROR;
500     }
501     auto opidx = primary_subgraph()->getInputs().at(index);
502     auto shape = primary_subgraph()->operands().at(opidx).shape();
503     if (isStatePreparedOrFinishedRun())
504       shape = _execution->getInputShape(onert::ir::IOIndex{index});
505     ti->rank = shape.rank();
506     for (int j = 0; j < ti->rank; ++j)
507     {
508       ti->dims[j] = shape.dim(j);
509     }
510     ti->dtype = datatype_to_nnfw_dtype(primary_subgraph()->operands().at(opidx).typeInfo().type());
511   }
512   catch (const std::exception &e)
513   {
514     std::cerr << "Error during nnfw_session::input_tensorinfo : " << e.what() << std::endl;
515     return NNFW_STATUS_ERROR;
516   }
517   return NNFW_STATUS_NO_ERROR;
518 }
519
520 NNFW_STATUS nnfw_session::output_tensorinfo(uint32_t index, nnfw_tensorinfo *ti)
521 {
522   if (isStateInitialized())
523     return NNFW_STATUS_INVALID_STATE;
524
525   if (ti == nullptr)
526   {
527     std::cerr << "Error during nnfw_session::output_tensorinfo, tensorinfo is null pointer."
528               << std::endl;
529     return NNFW_STATUS_UNEXPECTED_NULL;
530   }
531
532   if (index >= primary_subgraph()->getOutputs().size())
533   {
534     std::cerr << "Error during nnfw_session::output_tensorinfo, index is out of range."
535               << std::endl;
536     return NNFW_STATUS_ERROR;
537   }
538
539   try
540   {
541     auto opidx = primary_subgraph()->getOutputs().at(index);
542     auto shape = primary_subgraph()->operands().at(opidx).shape();
543     // If it is called after `nnfw_run` then get the shape from Execution, not from the graph
544     if (isStateFinishedRun())
545       shape = _execution->getOutputShape(onert::ir::IOIndex{index});
546     ti->rank = shape.rank();
547     for (int j = 0; j < ti->rank; ++j)
548     {
549       ti->dims[j] = shape.dim(j);
550     }
551     ti->dtype = datatype_to_nnfw_dtype(primary_subgraph()->operands().at(opidx).typeInfo().type());
552   }
553   catch (const std::exception &e)
554   {
555     std::cerr << "Error during nnfw_session::output_tensorinfo : " << e.what() << std::endl;
556     return NNFW_STATUS_ERROR;
557   }
558
559   return NNFW_STATUS_NO_ERROR;
560 }
561 NNFW_STATUS nnfw_session::register_custom_operation(const std::string &id,
562                                                     nnfw_custom_eval eval_func)
563 {
564   _kernel_registry->registerKernel(id, eval_func);
565   return NNFW_STATUS_NO_ERROR;
566 }
567
568 static std::string get_op_backend_string(std::string op)
569 {
570 #define MAP_MACRO(CircleName, OneRTName) {#CircleName, #OneRTName},
571
572   static std::unordered_map<std::string, std::string> operation_map = {
573 #include "OpMap.lst"
574   };
575
576 #undef MAP_MACRO
577
578   auto n = operation_map.find(op);
579
580   if (n == operation_map.end())
581   {
582     // this return value is handled by a caller to return error code
583     return std::string("");
584   }
585   else
586   {
587     return n->second;
588   }
589 }
590
591 NNFW_STATUS nnfw_session::set_available_backends(const char *backends)
592 {
593   if (!isStateModelLoaded())
594     return NNFW_STATUS_INVALID_STATE;
595
596   try
597   {
598     if (!backends)
599       return NNFW_STATUS_UNEXPECTED_NULL;
600     if (null_terminating(backends, MAX_BACKEND_NAME_LENGTH) == false)
601       return NNFW_STATUS_ERROR;
602
603     auto &options = _compiler->options();
604
605     using namespace onert::util;
606
607     options.backend_list = nnfw::misc::split(std::string{backends}, ';');
608   }
609   catch (const std::exception &e)
610   {
611     std::cerr << "Error during nnfw_session::set_available_backends : " << e.what() << std::endl;
612     return NNFW_STATUS_ERROR;
613   }
614   return NNFW_STATUS_NO_ERROR;
615 }
616
617 NNFW_STATUS nnfw_session::set_op_backend(const char *op, const char *backend)
618 {
619   if (!isStateModelLoaded())
620     return NNFW_STATUS_INVALID_STATE;
621
622   try
623   {
624     if (!op || !backend)
625       return NNFW_STATUS_UNEXPECTED_NULL;
626     if (!null_terminating(op, MAX_OP_NAME_LENGTH) ||
627         !null_terminating(backend, MAX_BACKEND_NAME_LENGTH))
628       return NNFW_STATUS_ERROR;
629
630     auto key = get_op_backend_string(op);
631
632     if (key.empty())
633     {
634       return NNFW_STATUS_ERROR;
635     }
636
637     auto &opcode_to_backend = _compiler->options().manual_scheduler_options.opcode_to_backend;
638     opcode_to_backend.emplace(onert::ir::toOpCode(key), backend);
639   }
640   catch (const std::exception &e)
641   {
642     std::cerr << "Error during nnfw_session::set_op_backend : " << e.what() << std::endl;
643     return NNFW_STATUS_ERROR;
644   }
645   return NNFW_STATUS_NO_ERROR;
646 }
647
648 NNFW_STATUS nnfw_session::set_config(const char *key, const char *value)
649 {
650   if (!isStateModelLoaded())
651     return NNFW_STATUS_INVALID_STATE;
652
653   if (!key || !value)
654     return NNFW_STATUS_UNEXPECTED_NULL;
655
656   auto &options = _compiler->options();
657
658   using namespace onert::util;
659
660   const std::string skey = key;
661
662   if (skey == config::TRACE_FILEPATH)
663   {
664     options.trace_filepath = value;
665   }
666   else if (skey == config::GRAPH_DOT_DUMP)
667   {
668     options.graph_dump_level = toInt(value);
669   }
670   else if (skey == config::OP_SEQ_MAX_NODE)
671   {
672     options.op_seq_max_node = toInt(value);
673   }
674   else if (skey == config::EXECUTOR)
675   {
676     options.executor = value;
677   }
678   else if (skey == config::OP_BACKEND_ALLOPS)
679   {
680     options.manual_scheduler_options.backend_for_all = value;
681   }
682   else if (skey == config::USE_SCHEDULER)
683   {
684     options.he_scheduler = toBool(value);
685   }
686   else if (skey == config::PROFILING_MODE)
687   {
688     options.he_profiling_mode = toBool(value);
689   }
690   else if (skey == config::DISABLE_COMPILE)
691   {
692     options.disable_compile = toBool(value);
693   }
694   else
695   {
696     return NNFW_STATUS_ERROR;
697   }
698   return NNFW_STATUS_NO_ERROR;
699 }
700
701 onert::ir::Graph *nnfw_session::primary_subgraph()
702 {
703   if (_subgraphs)
704   {
705     assert(!_execution);
706     return _subgraphs->primary().get();
707   }
708   else
709   {
710     assert(_execution);
711     // TODO Remove const_cast
712     // We assumed the graph will not change after compilation, but shape could change
713     return const_cast<onert::ir::Graph *>(&_execution->primary_subgraph());
714   }
715 }
716
717 NNFW_STATUS nnfw_session::get_config(const char *key, char *value, size_t value_size)
718 {
719   if (!isStateModelLoaded())
720     return NNFW_STATUS_INVALID_STATE;
721
722   if (!key || !value)
723     return NNFW_STATUS_UNEXPECTED_NULL;
724
725   auto &options = _compiler->options();
726
727   auto check_boundary = [](size_t dest_size, std::string &src) {
728     if (dest_size < src.length() + 1 /* for '\0' */)
729     {
730       std::cerr << "buffer is small to copy config value." << std::endl;
731       return false;
732     }
733     return true;
734   };
735
736   if (key == onert::util::config::BACKENDS)
737   {
738     if (options.backend_list.size() == 0)
739       return NNFW_STATUS_NO_ERROR; // no setting backend is not an error of get_config_str()
740
741     auto str = nnfw::misc::join(options.backend_list.begin(), options.backend_list.end(), ";");
742
743     if (!check_boundary(value_size, str))
744       return NNFW_STATUS_ERROR;
745
746     strncpy(value, str.c_str(), value_size);
747   }
748   else if (key == onert::util::config::EXECUTOR)
749   {
750     if (!check_boundary(value_size, options.executor))
751       return NNFW_STATUS_ERROR;
752
753     strncpy(value, options.executor.c_str(), options.executor.length());
754   }
755   else
756   {
757     return NNFW_STATUS_ERROR;
758   }
759
760   return NNFW_STATUS_NO_ERROR;
761 }
762
763 bool nnfw_session::isStateInitialized()
764 {
765   if (_state == State::INITIALIZED)
766   {
767     assert(!_subgraphs);
768     assert(!_compiler);
769     assert(!_execution);
770     return true;
771   }
772   else
773   {
774     return false;
775   }
776 }
777
778 bool nnfw_session::isStateModelLoaded()
779 {
780   if (_state == State::MODEL_LOADED)
781   {
782     assert(_subgraphs);
783     assert(_compiler);
784     assert(!_execution);
785     assert(!primary_subgraph()->isBuildingPhase());
786     return true;
787   }
788   else
789   {
790     return false;
791   }
792 }
793
794 bool nnfw_session::isStatePrepared()
795 {
796   if (_state == State::PREPARED)
797   {
798     assert(!_subgraphs);
799     assert(_compiler);
800     assert(_execution);
801     assert(!primary_subgraph()->isBuildingPhase());
802     return true;
803   }
804   else
805   {
806     return false;
807   }
808 }
809
810 bool nnfw_session::isStateRunning()
811 {
812   if (_state == State::RUNNING)
813   {
814     assert(!_subgraphs);
815     assert(_compiler);
816     assert(_execution);
817     assert(!primary_subgraph()->isBuildingPhase());
818     return true;
819   }
820   return false;
821 }
822
823 bool nnfw_session::isStateFinishedRun()
824 {
825   if (_state == State::FINISHED_RUN)
826   {
827     assert(!_subgraphs);
828     assert(_compiler);
829     assert(_execution);
830     assert(!primary_subgraph()->isBuildingPhase());
831     return true;
832   }
833   else
834   {
835     return false;
836   }
837 }
838
839 bool nnfw_session::isStatePreparedOrFinishedRun()
840 {
841   return isStatePrepared() || isStateFinishedRun();
842 }