bkryza / clang-uml

Customizable automatic UML diagram generator for C++ based on Clang.
Apache License 2.0
610 stars 44 forks source link

Segfault during generating diagrams #146

Closed xinbada007 closed 1 year ago

xinbada007 commented 1 year ago

Hi,

I am trying to generate diagrams for a project containing tens of thousands lines of codes. The codes are also compiled with many third-party like protobuf, boost, eigen, sqlite, ...

The project is also compiled using CMake + Clang under ubuntu 20.04, so complie_commands.json is provided.

Clang-UML 0.3.5 is installed and yaml is configed. During runing, clang-uml first prompted a few info showing compilation process and a few warnings, then it core dumped. Tried every diagram, only package diagram for a specified subfolder of the project got lucky.

bkryza commented 1 year ago

@xinbada007

If possible, please try to checkout latest master from clang-uml (I've fixed 2 known segfaults since 0.3.5), build it in debug mode:

git clone https://github.com/bkryza/clang-uml.git
cd clang-uml
make debug
export CLANG_UML_PATH=$PWD
cd <your project folder>
$CLANG_UML_PATH/debug/src/clang-uml -vvv -n your_class_diagram_name

Now if it crashes again, you should get stack trace in the terminal, please share it here along with your configuration file...

If you can't build it on your system, please at least rerun clang-uml using:

clang-uml -vvv -n your_class_diagram_name

and paste the end of the output from the terminal before crash and also your .clang-uml configuration file...

xinbada007 commented 1 year ago

1, clang-uml debug version

image

2, stack trace

Stack trace (most recent call last) in thread 1072727:

31 Source "/usr/include/c++/9/bits/std_function.h", line 300, in _M_invoke [0x5618f1b974c9]

    297:       static void
    298:       _M_invoke(const _Any_data& __functor, _ArgTypes&&... __args)
    299:       {
  > 300:    (*_Base::_M_get_pointer(__functor))(
    301:       std::forward<_ArgTypes>(__args)...);
    302:       }
    303:     };

30 Source "/data/Apps/clang-uml/src/common/generators/generators.cc", line 173, in operator() [0x5618f1b969ce]

    170:                 db = std::ref(*db), translation_units = valid_translation_units,
    171:                 verbose]() {
    172:                 try {
  > 173:                     generate_diagram(od, name, diagram, db, translation_units,
    174:                         generators, verbose != 0);
    175:                 }
    176:                 catch (std::runtime_error &e) {

29 Source "/data/Apps/clang-uml/src/common/generators/generators.cc", line 124, in generate_diagram [0x5618f1b966fc]

    121:     using clanguml::config::sequence_diagram;
    122: 
    123:     if (diagram->type() == diagram_t::kClass) {
  > 124:         detail::generate_diagram_impl<class_diagram>(
    125:             od, name, diagram, db, translation_units, generators, verbose);
    126:     }
    127:     else if (diagram->type() == diagram_t::kSequence) {

28 Source "/data/Apps/clang-uml/src/common/generators/generators.cc", line 91, in generate_diagram_impl [0x5618f1ba9ad1]

     88:     using diagram_model = typename diagram_model_t<DiagramConfig>::type;
     89:     using diagram_visitor = typename diagram_visitor_t<DiagramConfig>::type;
     90: 
  >  91:     auto model = clanguml::common::generators::generate<diagram_model,
     92:         diagram_config, diagram_visitor>(db, diagram->name,
     93:         dynamic_cast<diagram_config &>(*diagram), translation_units, verbose);

27 Source "/data/Apps/clang-uml/src/common/generators/generators.h", line 248, in generate<clanguml::class_diagram::model::diagram, clanguml::config::class_diagram, clanguml::class_diagram::visitor::translation_unit_visitor> [0x5618f1baf693]

    245:         std::make_unique<diagram_action_visitor_factory<DiagramModel,
    246:             DiagramConfig, DiagramVisitor>>(*diagram, config);
    247: 
  > 248:     auto res = clang_tool.run(action_factory.get());
    249: 
    250:     if (res != 0) {
    251:         throw std::runtime_error("Diagram " + name + " generation failed");

26 Object "/usr/lib/llvm-12/lib/libclang-cpp.so.12", at 0x7ff9111adb27, in clang::tooling::ClangTool::run(clang::tooling::ToolAction*)

25 Object "/usr/lib/llvm-12/lib/libclang-cpp.so.12", at 0x7ff9111aba73, in clang::tooling::ToolInvocation::run()

24 Object "/usr/lib/llvm-12/lib/libclang-cpp.so.12", at 0x7ff9111ac649, in clang::tooling::ToolInvocation::runInvocation(char const, clang::driver::Compilation, std::shared_ptr, std::shared_ptr)

23 Object "/usr/lib/llvm-12/lib/libclang-cpp.so.12", at 0x7ff9111ac8ec, in clang::tooling::FrontendActionFactory::runInvocation(std::shared_ptr, clang::FileManager, std::shared_ptr, clang::DiagnosticConsumer)d 1072727] [translation_unit_visitor.cc:691] == getQualifiedNameAsString() = boost::type_traits::detail::need_promotion

22 Object "/usr/lib/llvm-12/lib/libclang-cpp.so.12", at 0x7ff910fc8dd0, in clang::CompilerInstance::ExecuteAction(clang::FrontendAction&)

21 Object "/usr/lib/llvm-12/lib/libclang-cpp.so.12", at 0x7ff91103b117, in clang::FrontendAction::Execute()

20 Object "/usr/lib/llvm-12/lib/libclang-cpp.so.12", at 0x7ff90faa5d93, in clang::ParseAST(clang::Sema&, bool, bool)

19 Source "/data/Apps/clang-uml/src/common/generators/generators.h", line 156, in HandleTranslationUnit [0x5618f1bca30f]

    154:     void HandleTranslationUnit(clang::ASTContext &ast_context) override
    155:     {
  > 156:         visitor_.TraverseDecl(ast_context.getTranslationUnitDecl());
    157:         visitor_.finalize();
    158:     }
    159: };

18 Source "/usr/lib/llvm-12/include/clang/AST/DeclNodes.inc", line 601, in TraverseDecl [0x5618f1bcea5f]

    598: #ifndef TRANSLATIONUNIT
    599: #  define TRANSLATIONUNIT(Type, Base) DECL(Type, Base)
    600: #endif
  > 601: TRANSLATIONUNIT(TranslationUnit, Decl)
    602: #undef TRANSLATIONUNIT
    603: 
    604: LAST_DECL_RANGE(Decl, AccessSpec, TranslationUnit)

17 Source "/usr/lib/llvm-12/include/clang/AST/RecursiveASTVisitor.h", line 1490, in TraverseTranslationUnitDecl [0x5618f1bfaa0b]

   1487:   TRY_TO(TraverseStmt(D->getMessage()));
   1488: })
   1489: 
  >1490: DEF_TRAVERSE_DECL(
   1491:     TranslationUnitDecl,
   1492:     {// Code in an unnamed namespace shows up automatically in
   1493:      // decls_begin()/decls_end().  Thus we don't need to recurse on

16 Source "/usr/lib/llvm-12/include/clang/AST/RecursiveASTVisitor.h", line 1389, in TraverseDeclContextHelper [0x5618f1c18fe3]

   1387:   for (auto *Child : DC->decls()) {
   1388:     if (!canIgnoreChildDeclWhileTraversingDeclContext(Child))
  >1389:       TRY_TO(TraverseDecl(Child));
   1390:   }
   1391: 
   1392:   return true;

15 Source "/usr/lib/llvm-12/include/clang/AST/DeclNodes.inc", line 111, in TraverseDecl [0x5618f1bcdd35]

    108: #ifndef NAMESPACE
    109: #  define NAMESPACE(Type, Base) NAMED(Type, Base)
    110: #endif
  > 111: NAMESPACE(Namespace, NamedDecl)
    112: #undef NAMESPACE
    113: 
    114: #ifndef NAMESPACEALIAS

14 Source "/usr/lib/llvm-12/include/clang/AST/RecursiveASTVisitor.h", line 1514, in TraverseNamespaceDecl [0x5618f1bf1bad]

   1511: DEF_TRAVERSE_DECL(LabelDecl, {// There is no code in a LabelDecl.
   1512:                              })
   1513: 
  >1514: DEF_TRAVERSE_DECL(
   1515:     NamespaceDecl,
   1516:     {// Code in an unnamed namespace shows up automatically in
   1517:      // decls_begin()/decls_end().  Thus we don't need to recurse on

13 Source "/usr/lib/llvm-12/include/clang/AST/RecursiveASTVisitor.h", line 1389, in TraverseDeclContextHelper [0x5618f1c18fe3]

   1387:   for (auto *Child : DC->decls()) {
   1388:     if (!canIgnoreChildDeclWhileTraversingDeclContext(Child))
  >1389:       TRY_TO(TraverseDecl(Child));
   1390:   }
   1391: 
   1392:   return true;

12 Source "/usr/lib/llvm-12/include/clang/AST/DeclNodes.inc", line 111, in TraverseDecl [0x5618f1bcdd35]

    108: #ifndef NAMESPACE
    109: #  define NAMESPACE(Type, Base) NAMED(Type, Base)
    110: #endif
  > 111: NAMESPACE(Namespace, NamedDecl)
    112: #undef NAMESPACE
    113: 
    114: #ifndef NAMESPACEALIAS

11 Source "/usr/lib/llvm-12/include/clang/AST/RecursiveASTVisitor.h", line 1514, in TraverseNamespaceDecl [0x5618f1bf1bad]

   1511: DEF_TRAVERSE_DECL(LabelDecl, {// There is no code in a LabelDecl.
   1512:                              })
   1513: 
  >1514: DEF_TRAVERSE_DECL(
   1515:     NamespaceDecl,
   1516:     {// Code in an unnamed namespace shows up automatically in
   1517:      // decls_begin()/decls_end().  Thus we don't need to recurse on

10 Source "/usr/lib/llvm-12/include/clang/AST/RecursiveASTVisitor.h", line 1389, in TraverseDeclContextHelper [0x5618f1c18fe3]

   1387:   for (auto *Child : DC->decls()) {
   1388:     if (!canIgnoreChildDeclWhileTraversingDeclContext(Child))
  >1389:       TRY_TO(TraverseDecl(Child));
   1390:   }
   1391: 
   1392:   return true;

9 Source "/usr/lib/llvm-12/include/clang/AST/DeclNodes.inc", line 219, in TraverseDecl [0x5618f1bce003]

    216: #ifndef TYPEALIASTEMPLATE
    217: #  define TYPEALIASTEMPLATE(Type, Base) REDECLARABLETEMPLATE(Type, Base)
    218: #endif
  > 219: TYPEALIASTEMPLATE(TypeAliasTemplate, RedeclarableTemplateDecl)
    220: #undef TYPEALIASTEMPLATE
    221: 
    222: #ifndef VARTEMPLATE

8 Source "/usr/lib/llvm-12/include/clang/AST/RecursiveASTVisitor.h", line 1815, in TraverseTypeAliasTemplateDecl [0x5618f1bf38ee]

   1812:   // source.
   1813: })
   1814: 
  >1815: DEF_TRAVERSE_DECL(TypeAliasTemplateDecl, {
   1816:   TRY_TO(TraverseDecl(D->getTemplatedDecl()));
   1817:   TRY_TO(TraverseTemplateParameterListHelper(D->getTemplateParameters()));
   1818: })

7 Source "/usr/lib/llvm-12/include/clang/AST/DeclNodes.inc", line 219, in WalkUpFromTypeAliasTemplateDecl [0x5618f1c1f2ef]

    216: #ifndef TYPEALIASTEMPLATE
    217: #  define TYPEALIASTEMPLATE(Type, Base) REDECLARABLETEMPLATE(Type, Base)
    218: #endif
  > 219: TYPEALIASTEMPLATE(TypeAliasTemplate, RedeclarableTemplateDecl)
    220: #undef TYPEALIASTEMPLATE
    221: 
    222: #ifndef VARTEMPLATE

6 Source "/data/Apps/clang-uml/src/class_diagram/visitor/translation_unit_visitor.cc", line 260, in VisitTypeAliasTemplateDecl [0x5618f1b195fc]

    257:         return true;
    258: 
    259:     auto template_specialization_ptr =
  > 260:         tbuilder().build(cls, *template_type_specialization_ptr);
    261: 
    262:     if (!template_specialization_ptr)
    263:         return true;

5 Source "/data/Apps/clang-uml/src/class_diagram/visitor/template_builder.cc", line 209, in build [0x5618f1af9829]

    206:             base_index++;
    207:         }
    208: 
  > 209:     process_template_arguments(parent, cls, template_base_params,
    210:         template_type.template_arguments(), template_instantiation,
    211:         template_decl);

4 Source "/data/Apps/clang-uml/src/class_diagram/visitor/template_builder.cc", line 400, in process_template_arguments [0x5618f1afb9cb]

    397:         //
    398:         // Handle the template parameter/argument based on its kind
    399:         //
  > 400:         argument_process_dispatch(parent, cls, template_instantiation,
    401:             template_decl, arg, arg_index, arguments);
    402: 
    403:         if (arguments.empty()) {

3 Source "/data/Apps/clang-uml/src/class_diagram/visitor/template_builder.cc", line 453, in argument_process_dispatch [0x5618f1afc17f]

    450:         argument.push_back(process_null_argument(arg));
    451:         break;
    452:     case clang::TemplateArgument::Template:
  > 453:         argument.push_back(process_template_argument(arg));
    454:         break;
    455:     case clang::TemplateArgument::Type: {
    456:         argument.push_back(process_type_argument(parent, cls, template_decl,

2 Source "/data/Apps/clang-uml/src/class_diagram/visitor/template_builder.cc", line 490, in process_template_argument [0x5618f1afc842]

    487:     LOG_DBG("Processing template argument: {}", common::to_string(arg));
    488: 
    489:     auto arg_name =
  > 490:         arg.getAsTemplate().getAsTemplateDecl()->getQualifiedNameAsString();
    491: 
    492:     return template_parameter::make_template_type(arg_name);
    493: }

1 Object "/usr/lib/llvm-12/lib/libclang-cpp.so.12", at 0x7ff90fccac0c, in clang::NamedDecl::getQualifiedNameAsString[abi:cxx11]() const

0 Object "/usr/lib/llvm-12/lib/libclang-cpp.so.12", at 0x7ff90fcda001, in clang::Decl::getASTContext() const

Segmentation fault (Address not mapped to object [0x1c]) Segmentation fault (core dumped)

3, .clang-uml

image

bkryza commented 1 year ago

@xinbada007 Thank you for the details - I've patched this bug in master.

One more thing, in your config file you are using wildcards in your namespace filters, which are not yet supported, please change your include filters to:

include:
  namespace:
    - project::namespace
  context:
    - project::namespace::class

and remove your exclude filter - if you provide an include namespace filter, it will only include entities from this namespace, you don't need to exclude any other namespaces unless they are sub namespaces of project::namespace.

Please try again, and if it still crashes please paste the stack trace...

bkryza commented 1 year ago

@xinbada007 Also, since you mentioned this is a large code base, try to use the glob pattern to limit the number of source files that are visited for each diagram. In particular, if you're generating a context of a specific class, usually you only need to add the source file for this class, as all headers necessary to generate the context will be traversed anyway through #include directives, i.e.:

glob:
  - src/my_class.cpp

This will significantly decrease the generation times...

xinbada007 commented 1 year ago

@bkryza oh man it's working! I got a large puml(class diagram) like 150KB~ (only used a single .cc as you suggested). Now I got trouble viewing it. it's too slow to view in vs-code with plantuml. I tried with official plantuml.jar and it can't even print the graph right (only a small part of the whole picture compared to that in vs-code with plantuml).

I would appreciate any suggestions regarding visualization tools (especially for large uml).

bkryza commented 1 year ago

@xinbada007 Glad it's working.

First of all, try to limit the amount of classes you have in one diagram using the include/exclude filters. Actually the context diagram should ensure that the diagram only contains elements in direct relationship to mentioned class, but there still can be a lot of them...

Second of all, if you have a lot of dependency relationships but you don't need them, you can remove them from the diagram using:

exclude:
  relationships:
    - dependency

Next, you can also remove private and protected members and methods from the class descriptions using:

exclude:
  access:
    - protected
    - private

If you really need such large diagrams, then do not generate PNG but SVG instead:

plantuml -tsvg docs/diagrams/localview.puml

This will ensure that the entire diagram is rendered.

You can then view it using your web browser or most generic image viewers on Linux such as gwenview.