[Tizen] Print backtrace when an exception occurs
[platform/core/uifw/dali-core.git] / dali / public-api / common / dali-common.cpp
1 /*
2  * Copyright (c) 2021 Samsung Electronics Co., Ltd.
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
18 // CLASS HEADER
19 #include <dali/public-api/common/dali-common.h>
20
21 // EXTERNAL INCLUDES
22 #include <stdlib.h>
23 #include <cstdio>
24 #include <string>
25
26 #define BACKTRACE_ENABLED
27
28 #if defined(BACKTRACE_ENABLED)
29 #if defined(__GLIBC__)
30 #include <execinfo.h>
31 #endif
32 #include <cxxabi.h>
33 #endif
34 #include <cstring>
35
36 // INTERNAL INCLUDES
37 #include <dali/integration-api/debug.h>
38
39 namespace Dali
40 {
41 #if defined(BACKTRACE_ENABLED)
42
43 namespace
44 {
45 const int32_t MAX_NUM_STACK_FRAMES = 25;
46 }
47
48 std::string Demangle(const char* symbol)
49 {
50   std::string result;
51
52   // backtrace ::=  <filename>'('['_'<mangled-symbol>'_']['+'<offset>]')'
53   // Only want <mangled-symbol>:
54   const char* openParen = strchr(symbol, '(');
55   if(openParen != NULL)
56   {
57     const char* startOfToken = openParen + 1;
58     const char* plus         = strchr(startOfToken, '+');
59     const char* closeParen   = strchr(startOfToken, ')');
60     const char* endOfToken   = NULL;
61     if(plus != NULL)
62     {
63       endOfToken = plus;
64     }
65     else if(closeParen != NULL)
66     {
67       endOfToken = closeParen;
68     }
69     if(endOfToken != NULL)
70     {
71       size_t tokenLength = endOfToken - startOfToken;
72
73       // Allocate space for symbol
74       char* mangledSymbol = reinterpret_cast<char*>(malloc(tokenLength + 1u));
75       if(mangledSymbol != NULL)
76       {
77         strncpy(mangledSymbol, startOfToken, tokenLength);
78         mangledSymbol[tokenLength] = '\0';
79
80         size_t  size;
81         int32_t status;
82         char*   demangled = NULL;
83         demangled         = abi::__cxa_demangle(mangledSymbol, NULL, &size, &status);
84         if(demangled != NULL)
85         {
86           result = demangled;
87           free(demangled); // demangle() allocates returned string, so free it
88         }
89         else
90         {
91           result = symbol;
92         }
93         free(mangledSymbol);
94       }
95     }
96   }
97
98   return result;
99 }
100
101 DALI_CORE_API DaliException::DaliException(const char* location, const char* condition)
102 : location(location),
103   condition(condition)
104 {
105   // Note, if a memory error has occured, then the backtrace won't work - backtrace_symbols relies on
106   // allocating memory.
107
108   // Initial dlog error message is output in DALI_ASSERT_ALWAYS macro
109   // Also output on stderr
110 #if defined(DEBUG_ENABLED)
111   fprintf(stderr, "Exception: \n%s\n thrown at %s\nSee dlog for backtrace\n", condition, location);
112 #else
113   fprintf(stderr, "Exception: \n%s\n thrown\nSee dlog for backtrace\n", condition);
114 #endif
115
116   DALI_LOG_ERROR_NOFN("Backtrace:\n");
117
118   void*   frameArray[MAX_NUM_STACK_FRAMES];
119   int32_t nSize   = backtrace(frameArray, MAX_NUM_STACK_FRAMES);
120   char**  symbols = backtrace_symbols(frameArray, nSize);
121   for(int32_t i = 1; i < nSize; i++)
122   {
123     std::string demangled_symbol = Demangle(symbols[i]);
124     DALI_LOG_ERROR_NOFN("[%02d]   %s\n", i, demangled_symbol.c_str());
125   }
126   free(symbols);
127 }
128
129 #else // BACKTRACE_ENABLED
130
131 DALI_CORE_API DaliException::DaliException(const char* location, const char* condition)
132 : location(location),
133   condition(condition)
134 {
135 #if defined(DEBUG_ENABLED)
136   printf("Exception: \n%s\n thrown at %s\n", condition, location);
137 #else
138   printf("Exception: \n%s\n thrown\n", condition);
139 #endif
140 }
141
142 #endif // BACKTRACE_ENABLED
143
144 DALI_CORE_API void DaliAssertMessage(const char* location, const char* condition)
145 {
146 #if defined(DEBUG_ENABLED)
147   DALI_LOG_ERROR_NOFN("Assert (%s) failed in: %s\n", condition, location);
148 #else
149   DALI_LOG_ERROR_NOFN("Assert (%s) failed\n", condition);
150 #endif
151 }
152
153 } // namespace Dali