KiCad PCB EDA Suite
Loading...
Searching...
No Matches
app_monitor.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright The KiCad Developers, see AUTHORS.TXT for contributors.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, you may find one here:
18 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
19 * or you may search the http://www.gnu.org website for the version 2 license,
20 * or you may write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
22 */
23
24#include <app_monitor.h>
25#include <pgm_base.h>
26#include <magic_enum.hpp>
27#include <kiplatform/policy.h>
28#include <policy_keys.h>
29#include <paths.h>
30#include <wx/filename.h>
31#include <wx/ffile.h>
32
33#include <boost/uuid/uuid_io.hpp>
34#include <boost/uuid/uuid_generators.hpp>
35#include <build_version.h>
36
37#ifdef KICAD_USE_SENTRY
38#include <sentry.h>
39#endif
40
41using namespace APP_MONITOR;
42
44 m_isOptedIn( false )
45{
46}
47
48
50{
51#ifdef KICAD_USE_SENTRY
52 sentryInit();
53#endif
54}
55
56
58{
59#ifdef KICAD_USE_SENTRY
60 sentry_close();
61#endif
62}
63
64
65void SENTRY::AddTag( const wxString& aKey, const wxString& aValue )
66{
67#ifdef KICAD_USE_SENTRY
68 sentry_set_tag( aKey.c_str(), aValue.c_str() );
69#endif
70}
71
72
73void SENTRY::SetSentryOptIn( bool aOptIn )
74{
75#ifdef KICAD_USE_SENTRY
76 if( aOptIn )
77 {
78 // Opting in (when not opted in before) may require reading the uid off disk
79 // as it was skipped previously.
81
82 if( !m_sentry_optin_fn.Exists() )
83 {
84 wxFFile sentryInitFile( m_sentry_optin_fn.GetFullPath(), "w" );
85 sentryInitFile.Write( "" );
86 sentryInitFile.Close();
87 }
88
89 m_isOptedIn = true;
90 }
91 else
92 {
93 if( m_sentry_optin_fn.Exists() )
94 {
95 wxRemoveFile( m_sentry_optin_fn.GetFullPath() );
96 sentry_close();
97 }
98
99 m_isOptedIn = false;
100 }
101#else
102 m_isOptedIn = false;
103#endif
104}
105
106
108{
109 boost::uuids::uuid uuid = boost::uuids::random_generator()();
110 wxString userGuid = boost::uuids::to_string( uuid );
111
112 wxFFile sentryInitFile( m_sentry_uid_fn.GetFullPath(), "w" );
113 sentryInitFile.Write( userGuid );
114 sentryInitFile.Close();
115
116 return userGuid;
117}
118
119
124
125
126const wxString& SENTRY::GetSentryId()
127{
128 return m_sentryUid;
129}
130
131
133{
134 if( m_sentry_optin_fn.Exists() )
135 {
136 wxFFile sentryInitFile( m_sentry_uid_fn.GetFullPath() );
137 sentryInitFile.ReadAll( &m_sentryUid );
138 sentryInitFile.Close();
139 }
140
141 if( m_sentryUid.IsEmpty() || m_sentryUid.length() != 36 )
142 {
144 }
145}
146
147
149{
150#ifdef KICAD_USE_SENTRY
151 m_sentry_optin_fn = wxFileName( PATHS::GetUserCachePath(), "sentry-opt-in" );
152 m_sentry_uid_fn = wxFileName( PATHS::GetUserCachePath(), "sentry-uid" );
153
154 if( isConfiguredOptedIn() )
155 {
156 m_isOptedIn = true;
157
159
160 sentry_options_t* options = sentry_options_new();
161
162#ifndef KICAD_SENTRY_DSN
163#error "Project configuration error, missing KICAD_SENTRY_DSN"
164#endif
165 // only capture 5% of transactions
166 sentry_options_set_traces_sample_rate( options, 0.05 );
167 sentry_options_set_dsn( options, KICAD_SENTRY_DSN );
168
169 wxFileName tmp;
170 tmp.AssignDir( PATHS::GetUserCachePath() );
171 tmp.AppendDir( "sentry" );
172
173#ifdef __WINDOWS__
174 sentry_options_set_database_pathw( options, tmp.GetPathWithSep().wc_str() );
175#else
176 sentry_options_set_database_path( options, tmp.GetPathWithSep().c_str() );
177#endif
178 sentry_options_set_symbolize_stacktraces( options, true );
179 sentry_options_set_auto_session_tracking( options, false );
180
181 sentry_options_set_release( options, GetCommitHash().ToStdString().c_str() );
182
183 // This just gives us more filtering within sentry, issues still get grouped across
184 // environments.
185 sentry_options_set_environment( options, GetMajorMinorVersion().c_str() );
186
187 sentry_init( options );
188
189 sentry_value_t user = sentry_value_new_object();
190 sentry_value_set_by_key( user, "id", sentry_value_new_string( m_sentryUid.c_str() ) );
191 sentry_set_user( user );
192
193 sentry_set_tag( "kicad.version", GetBuildVersion().ToStdString().c_str() );
194 }
195#endif
196}
197
198
200{
201 // policy takes precedence
204 {
205 return policyState == KIPLATFORM::POLICY::PBOOL::ENABLED;
206 }
207
208 return m_sentry_optin_fn.Exists();
209}
210
211
213{
214#ifdef KICAD_USE_SENTRY
215 // always override with policy, just in case
218 {
219 return policyState == KIPLATFORM::POLICY::PBOOL::ENABLED;
220 }
221
222 return m_isOptedIn;
223#else
224 return false;
225#endif
226}
227
228
229void SENTRY::LogAssert( const ASSERT_CACHE_KEY& aKey, const wxString& aAssertMsg )
230{
231#ifdef KICAD_USE_SENTRY
233 {
234 return;
235 }
236
237 // We use an assert cache to avoid collecting too many events
238 // Because they can come from functions that are called hundreds of times in loops
239 if( m_assertCache.find( aKey ) == m_assertCache.end() )
240 {
241 sentry_value_t exc = sentry_value_new_exception( "assert", aAssertMsg.c_str() );
242 sentry_value_set_stacktrace( exc, NULL, 0 );
243
244 sentry_value_t sentryEvent = sentry_value_new_event();
245 sentry_event_add_exception( sentryEvent, exc );
246 sentry_capture_event( sentryEvent );
247 m_assertCache.insert( aKey );
248 }
249#endif
250}
251
252
253void SENTRY::LogException( const wxString& aMsg )
254{
255#ifdef KICAD_USE_SENTRY
257 {
258 return;
259 }
260
261 sentry_value_t exc = sentry_value_new_exception( "exception", aMsg.c_str() );
262 sentry_value_set_stacktrace( exc, NULL, 0 );
263
264 sentry_value_t sentryEvent = sentry_value_new_event();
265 sentry_event_add_exception( sentryEvent, exc );
266 sentry_capture_event( sentryEvent );
267#endif
268}
269
270#ifdef KICAD_USE_SENTRY
271static std::string GetSentryBreadCrumbType( BREADCRUMB_TYPE aType )
272{
273 // the only special case due to collisions with defines
274 if( aType == BREADCRUMB_TYPE::DBG )
275 return "debug";
276
277 if( aType == BREADCRUMB_TYPE::ERR )
278 return "error";
279
280 std::string ret( magic_enum::enum_name( aType ) );
281
282 std::transform( ret.begin(), ret.end(), ret.begin(),
283 []( unsigned char c )
284 {
285 return std::tolower( c );
286 } );
287
288 return ret;
289}
290
291
292static std::string GetSentryBreadCrumbLevel( BREADCRUMB_LEVEL aLevel )
293{
294 // the only special case due to collisions with defines
295 if( aLevel == BREADCRUMB_LEVEL::DBG )
296 return "debug";
297
298 if( aLevel == BREADCRUMB_LEVEL::ERR )
299 return "error";
300
301 std::string ret( magic_enum::enum_name( aLevel ) );
302
303 std::transform( ret.begin(), ret.end(), ret.begin(),
304 []( unsigned char c )
305 {
306 return std::tolower( c );
307 } );
308
309 return ret;
310}
311#endif
312
313namespace APP_MONITOR
314{
315SENTRY* SENTRY::m_instance = nullptr;
316
317
318bool operator<( const ASSERT_CACHE_KEY& aKey1, const ASSERT_CACHE_KEY& aKey2 )
319{
320 return aKey1.file < aKey2.file || aKey1.line < aKey2.line || aKey1.func < aKey2.func || aKey1.cond < aKey2.cond;
321}
322
323
324void AddBreadcrumb( BREADCRUMB_TYPE aType, const wxString& aMsg, const wxString& aCategory, BREADCRUMB_LEVEL aLevel )
325{
326#ifdef KICAD_USE_SENTRY
327 if( !SENTRY::Instance()->IsOptedIn() )
328 {
329 return;
330 }
331
332 std::string type = GetSentryBreadCrumbType( aType );
333 sentry_value_t crumb = sentry_value_new_breadcrumb( type.c_str(), aMsg.c_str() );
334
335 sentry_value_set_by_key( crumb, "category", sentry_value_new_string( aCategory.c_str() ) );
336
337 if( aType == BREADCRUMB_TYPE::ERR )
338 {
339 std::string level = GetSentryBreadCrumbLevel( aLevel );
340 sentry_value_set_by_key( crumb, "level", sentry_value_new_string( level.c_str() ) );
341 }
342
343 sentry_add_breadcrumb( crumb );
344#endif
345}
346
347
348void AddNavigationBreadcrumb( const wxString& aMsg, const wxString& aCategory )
349{
350#ifdef KICAD_USE_SENTRY
352#endif
353}
354
355
356void AddTransactionBreadcrumb( const wxString& aMsg, const wxString& aCategory )
357{
358#ifdef KICAD_USE_SENTRY
360#endif
361}
362
363#ifdef KICAD_USE_SENTRY
364class TRANSACTION_IMPL
365{
366public:
367 TRANSACTION_IMPL( const std::string& aName, const std::string& aOperation )
368 {
369 m_ctx = sentry_transaction_context_new( aName.c_str(), aOperation.c_str() );
370 }
371
372 ~TRANSACTION_IMPL() {
373 Finish();
374
375 // note m_ctx is handled by sentry
376 }
377
378 void Start()
379 {
380 m_tx = sentry_transaction_start( m_ctx, sentry_value_new_null() );
381 }
382
383 void Finish()
384 {
385 FinishSpan();
386
387 if( m_tx )
388 {
389 sentry_transaction_finish( m_tx );
390 m_tx = nullptr;
391 }
392 }
393
394 void StartSpan( const std::string& aOperation, const std::string& aDescription )
395 {
396 if( m_span )
397 return;
398
399 if( !m_tx )
400 return;
401
402 m_span = sentry_transaction_start_child( m_tx, aOperation.c_str(), aDescription.c_str() );
403 }
404
405 void FinishSpan()
406 {
407 if( m_span )
408 {
409 sentry_span_finish( m_span );
410 m_span = nullptr;
411 }
412 }
413
414private:
415 sentry_transaction_context_t* m_ctx = nullptr;
416 sentry_transaction_t* m_tx = nullptr;
417 sentry_span_t* m_span = nullptr;
418};
419#endif
420}
421
422
423TRANSACTION::TRANSACTION( const std::string& aName, const std::string& aOperation )
424{
425#ifdef KICAD_USE_SENTRY
426 if( SENTRY::Instance()->IsOptedIn() )
427 {
428 m_impl = new TRANSACTION_IMPL( aName, aOperation );
429 }
430#endif
431}
432
433
435{
436#ifdef KICAD_USE_SENTRY
437 delete m_impl;
438#endif
439}
440
441
443{
444#ifdef KICAD_USE_SENTRY
445 if( m_impl )
446 {
447 m_impl->Start();
448 }
449#endif
450}
451
452
453void TRANSACTION::StartSpan( const std::string& aOperation, const std::string& aDescription )
454{
455#ifdef KICAD_USE_SENTRY
456 if( m_impl )
457 {
458 m_impl->StartSpan( aOperation, aDescription );
459 }
460#endif
461}
462
463
465{
466#ifdef KICAD_USE_SENTRY
467 if( m_impl )
468 {
469 m_impl->Finish();
470 }
471#endif
472}
473
474
476{
477#ifdef KICAD_USE_SENTRY
478 if( m_impl )
479 {
480 m_impl->FinishSpan();
481 }
482#endif
483}
wxString GetMajorMinorVersion()
Get only the major and minor version in a string major.minor.
wxString GetCommitHash()
Get the commit hash as a string.
wxString GetBuildVersion()
Get the full KiCad version string.
This is a singleton class intended to manage sentry.
std::set< ASSERT_CACHE_KEY > m_assertCache
wxString sentryCreateUid()
wxFileName m_sentry_uid_fn
void LogAssert(const ASSERT_CACHE_KEY &aKey, const wxString &aMsg)
void SetSentryOptIn(bool aOptIn)
void LogException(const wxString &aMsg)
void AddTag(const wxString &aKey, const wxString &aValue)
static SENTRY * m_instance
wxFileName m_sentry_optin_fn
static SENTRY * Instance()
const wxString & GetSentryId()
void StartSpan(const std::string &aOperation, const std::string &aDescription)
TRANSACTION(const std::string &aName, const std::string &aOperation)
static wxString GetUserCachePath()
Gets the stock (install) 3d viewer plugins path.
Definition paths.cpp:419
void AddTransactionBreadcrumb(const wxString &aMsg, const wxString &aCategory)
Add a transaction breadcrumb.
void AddBreadcrumb(BREADCRUMB_TYPE aType, const wxString &aMsg, const wxString &aCategory, BREADCRUMB_LEVEL aLevel)
Add a sentry breadcrumb.
void AddNavigationBreadcrumb(const wxString &aMsg, const wxString &aCategory)
Add a navigation breadcrumb.
bool operator<(const ASSERT_CACHE_KEY &aKey1, const ASSERT_CACHE_KEY &aKey2)
PBOOL GetPolicyBool(const wxString &aKey)
see class PGM_BASE
#define POLICY_KEY_DATACOLLECTION
Definition policy_keys.h:30
This struct represents a key being used for the std::set that deduplicates asserts during this runnin...
Definition app_monitor.h:90