[dali_2.3.38] Merge branch 'devel/master'
[platform/core/uifw/dali-core.git] / dali / integration-api / debug.cpp
1 /*
2  * Copyright (c) 2024 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/integration-api/debug.h>
20
21 // EXTERNAL INCLUDES
22 #include <chrono>
23 #include <cstdarg>
24 #include <memory>
25
26 namespace Dali
27 {
28 #ifdef DEBUG_ENABLED
29
30 // Fake globals for gdb typedefs
31 Dali::DebugPropertyValueArray gValueArray;
32 Dali::DebugPropertyValueMap   gValueMap;
33
34 #endif
35
36 namespace Integration
37 {
38 namespace Log
39 {
40 namespace
41 {
42 void FormatPrintToStandardOutput(DebugPriority priority, const char* format, va_list args)
43 {
44   if(format != nullptr)
45   {
46     char* buffer       = nullptr;
47     int   bufferLength = vasprintf(&buffer, format, args); // Development note : bufferLength doesn't include null-terminated character.
48
49     if(bufferLength >= 0) // No error
50     {
51       // TODO : We need to consider thread-safety way to print something.
52       switch(priority)
53       {
54         case DebugPriority::DEBUG:
55         case DebugPriority::INFO:
56         {
57           fprintf(stdout, "%.*s", bufferLength, buffer);
58           break;
59         }
60         case DebugPriority::WARNING:
61         case DebugPriority::ERROR:
62         default:
63         {
64           fprintf(stderr, "%.*s", bufferLength, buffer);
65           break;
66         }
67       }
68       free(buffer);
69     }
70   }
71 }
72 } // namespace
73 thread_local LogFunction gthreadLocalLogFunction = nullptr;
74
75 /* Forward declarations */
76 std::string FormatToString(const char* format, ...);
77 std::string ArgListToString(const char* format, va_list args);
78
79 void LogMessage(DebugPriority priority, const char* format, ...)
80 {
81   if(DALI_UNLIKELY(!gthreadLocalLogFunction))
82   {
83     va_list arg;
84     va_start(arg, format);
85     FormatPrintToStandardOutput(priority, format, arg);
86     va_end(arg);
87   }
88   else
89   {
90     va_list arg;
91     va_start(arg, format);
92     std::string message = ArgListToString(format, arg);
93     va_end(arg);
94
95     gthreadLocalLogFunction(priority, message);
96   }
97 }
98
99 void InstallLogFunction(const LogFunction& logFunction)
100 {
101   // TLS stores a pointer to an object.
102   // It needs to be allocated on the heap, because TLS will destroy it when the thread exits.
103
104   gthreadLocalLogFunction = logFunction;
105 }
106
107 void UninstallLogFunction()
108 {
109   gthreadLocalLogFunction = nullptr;
110 }
111
112 #ifdef DEBUG_ENABLED
113
114 /*Change false to true if trace is needed but don't commit to codeline*/
115 Filter* Filter::gRender     = Filter::New(Debug::Concise, false, "LOG_RENDER");
116 Filter* Filter::gResource   = Filter::New(Debug::Concise, false, "LOG_RESOURCE");
117 Filter* Filter::gGLResource = Filter::New(Debug::Concise, false, "LOG_GL_RESOURCE");
118 Filter* Filter::gObject     = nullptr;
119 Filter* Filter::gImage      = Filter::New(Debug::Concise, false, "LOG_IMAGE");
120 Filter* Filter::gModel      = Filter::New(Debug::Concise, false, "LOG_MODEL");
121 Filter* Filter::gNode       = nullptr;
122 Filter* Filter::gElement    = nullptr;
123 Filter* Filter::gActor      = Filter::New(Debug::Concise, false, "LOG_ACTOR");
124 Filter* Filter::gShader     = Filter::New(Debug::Concise, false, "LOG_SHADER");
125
126 typedef std::list<std::unique_ptr<Filter>>           FilterList;
127 typedef std::list<std::unique_ptr<Filter>>::iterator FilterIter;
128
129 namespace
130 {
131 static FilterList& GetActiveFilters()
132 {
133   static FilterList activeFilters;
134   return activeFilters;
135 }
136 } // namespace
137
138 Filter* Filter::New(LogLevel level, bool trace, const char* environmentVariableName)
139 {
140   char* environmentVariableValue = getenv(environmentVariableName);
141   if(environmentVariableValue)
142   {
143     unsigned int envLevel(0);
144     char         envTraceString(0);
145     sscanf(environmentVariableValue, "%u,%c", &envLevel, &envTraceString);
146
147     if(envLevel > Verbose)
148     {
149       envLevel = Verbose;
150     }
151     level = LogLevel(envLevel);
152
153     // Just use 'f' and 't' as it's faster than doing full string comparisons
154     if(envTraceString == 't')
155     {
156       trace = true;
157     }
158     else if(envTraceString == 'f')
159     {
160       trace = false;
161     }
162   }
163
164   Filter* filter = new Filter(level, trace);
165   filter->mNesting++;
166   GetActiveFilters().push_back(std::unique_ptr<Filter>(filter));
167   return filter;
168 }
169
170 /**
171  * Enable trace on all filters.
172  */
173 void Filter::EnableGlobalTrace()
174 {
175   for(FilterIter iter = GetActiveFilters().begin(); iter != GetActiveFilters().end(); iter++)
176   {
177     (*iter)->EnableTrace();
178   }
179 }
180
181 /**
182  * Disable trace on all filters.
183  */
184 void Filter::DisableGlobalTrace()
185 {
186   for(FilterIter iter = GetActiveFilters().begin(); iter != GetActiveFilters().end(); iter++)
187   {
188     (*iter)->DisableTrace();
189   }
190 }
191
192 void Filter::SetGlobalLogLevel(LogLevel level)
193 {
194   for(FilterIter iter = GetActiveFilters().begin(); iter != GetActiveFilters().end(); iter++)
195   {
196     (*iter)->SetLogLevel(level);
197   }
198 }
199
200 void Filter::Log(LogLevel level, const char* format, ...)
201 {
202   if(level <= mLoggingLevel)
203   {
204     va_list arg;
205     va_start(arg, format);
206
207     if(mTraceEnabled)
208     {
209       char* buffer   = nullptr;
210       int   numChars = asprintf(&buffer, "    %-*c %s", mNesting, ':', format);
211       if(numChars >= 0) // No error
212       {
213         std::string message = ArgListToString(buffer, arg);
214         LogMessage(INFO, message.c_str());
215         free(buffer);
216       }
217     }
218     else
219     {
220       std::string message = ArgListToString(format, arg);
221       LogMessage(INFO, message.c_str());
222     }
223     va_end(arg);
224   }
225 }
226
227 TraceObj::TraceObj(Filter* filter, const char* format, ...)
228 : mFilter(filter)
229 {
230   if(mFilter && mFilter->IsTraceEnabled())
231   {
232     va_list arg;
233     va_start(arg, format);
234     mMessage = ArgListToString(format, arg);
235     va_end(arg);
236
237     LogMessage(INFO, "Entr%-*c %s\n", mFilter->mNesting, ':', mMessage.c_str());
238     ++mFilter->mNesting;
239   }
240 }
241
242 TraceObj::~TraceObj()
243 {
244   if(mFilter && mFilter->IsTraceEnabled())
245   {
246     if(mFilter->mNesting)
247     {
248       --mFilter->mNesting;
249     }
250     LogMessage(INFO, "Exit%-*c %s\n", mFilter->mNesting, ':', mMessage.c_str());
251   }
252 }
253
254 #endif // DEBUG_ENABLED
255
256 std::string ArgListToString(const char* format, va_list args)
257 {
258   std::string str; // empty string
259   if(format != nullptr)
260   {
261     char* buffer = nullptr;
262     int   err    = vasprintf(&buffer, format, args);
263     if(err >= 0) // No error
264     {
265       str = buffer;
266       free(buffer);
267     }
268   }
269   return str;
270 }
271
272 std::string FormatToString(const char* format, ...)
273 {
274   va_list arg;
275   va_start(arg, format);
276   std::string s = ArgListToString(format, arg);
277   va_end(arg);
278   return s;
279 }
280
281 #ifdef DEBUG_ENABLED
282
283 void GetNanoseconds(uint64_t& timeInNanoseconds)
284 {
285   // Get the time of a monotonic clock since its epoch.
286   auto epoch = std::chrono::steady_clock::now().time_since_epoch();
287
288   auto duration = std::chrono::duration_cast<std::chrono::nanoseconds>(epoch);
289
290   timeInNanoseconds = static_cast<uint64_t>(duration.count());
291 }
292
293 #endif // DEBUG_ENABLED
294
295 } // namespace Log
296
297 } // namespace Integration
298
299 } // namespace Dali