• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

nasa / trick / 15123805174

19 May 2025 09:44PM UTC coverage: 55.797%. First build
15123805174

Pull #1847

github

web-flow
Merge 0443c4fd5 into ff58ed109
Pull Request #1847: Add logging var multiple times check.

4 of 71 new or added lines in 2 files covered. (5.63%)

12334 of 22105 relevant lines covered (55.8%)

258257.83 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

46.7
/trick_source/sim_services/DataRecord/DataRecordDispatcher.cpp
1
/*
2
   PURPOSE: (Copy data out of structures into data recording memory.)
3
   PROGRAMMERS:
4
     (((Robert W. Bailey) (LinCom Corp) (3/96) (SES upgrades))
5
      ((Alex Lin) (NASA) (6/02) (DR_changes upgrade)))
6
 */
7

8
#include <iostream>
9
#include <sstream>
10
#include <stdlib.h>
11
#include <unistd.h>
12
#include <algorithm>
13

14
#include <signal.h>
15
#if __linux
16
#include <sys/syscall.h>
17
#endif
18

19
#include "trick/DataRecordDispatcher.hh"
20
#include "trick/exec_proto.h"
21
#include "trick/exec_proto.hh"
22
#include "trick/message_proto.h"
23
#include "trick/message_type.h"
24
#include "trick/command_line_protos.h"
25

26
Trick::DataRecordDispatcher * the_drd = NULL ;
27

28
Trick::DRDMutexes::DRDMutexes() {
181✔
29
    pthread_cond_init(&dr_go_cv, NULL);
181✔
30
    pthread_mutex_init(&dr_go_mutex, NULL);
181✔
31
    pthread_cond_init(&init_complete_cv, NULL);
181✔
32
    pthread_mutex_init(&init_complete_mutex, NULL);
181✔
33
    cancelled = false;
181✔
34
}
181✔
35

36
Trick::DRDWriterThread::DRDWriterThread(DRDMutexes & in_mutexes, std::vector <Trick::DataRecordGroup *> & in_groups) :
181✔
37
 SysThread("DR_Writer"),
38
 drd_mutexes(in_mutexes) ,
39
 groups(in_groups) {}
181✔
40

41
void * Trick::DRDWriterThread::thread_body() {
149✔
42
    pthread_mutex_lock(&(drd_mutexes.dr_go_mutex));
149✔
43

44
    /* tell the main thread that the writer is ready to go */
45
    pthread_mutex_lock(&(drd_mutexes.init_complete_mutex));
149✔
46
    pthread_cond_signal(&(drd_mutexes.init_complete_cv));
149✔
47
    pthread_mutex_unlock(&(drd_mutexes.init_complete_mutex));
149✔
48

49
    /* from now until death, wait for the condition variable,
50
       then call the write_data method for all of the groups */
51
    while(1) {
52
        pthread_cond_wait(&(drd_mutexes.dr_go_cv), &(drd_mutexes.dr_go_mutex));
46,209✔
53
        if (drd_mutexes.cancelled) {
46,209✔
54
            pthread_mutex_unlock(&(drd_mutexes.dr_go_mutex));
149✔
55
            pthread_exit(0);
149✔
56
        }
57
        for ( unsigned int ii = 0 ; ii < groups.size() ; ii++ ) {
173,329✔
58
            if ( groups[ii]->buffer_type == Trick::DR_Buffer ) {
127,269✔
59
                groups[ii]->write_data(true) ;
44,652✔
60
            }
61
        }
62
    }
46,060✔
63
    pthread_mutex_unlock(&(drd_mutexes.dr_go_mutex));
64
    return NULL ;
65
}
66

67
void Trick::DRDWriterThread::dump( std::ostream & oss ) {
×
68
    oss << "Trick::DRDWriterThread (" << name << ")" << std::endl ;
×
69
    oss << "    number of data record groups = " << groups.size() << std::endl ;
×
70
    Trick::ThreadBase::dump(oss) ;
×
71
}
×
72

73
Trick::DataRecordDispatcher::DataRecordDispatcher()
181✔
74
    : drd_writer_thread(drd_mutexes, groups),
181✔
75
      verify_log_vars(true)
181✔
76
{
77
    the_drd = this;
181✔
78
}
181✔
79

80
Trick::DataRecordDispatcher::~DataRecordDispatcher() {
181✔
81
}
181✔
82

83
int Trick::DataRecordDispatcher::remove_files() {
158✔
84

85
    std::string command;
158✔
86
    command = std::string("/bin/rm -rf ") + command_line_args_get_output_dir() + std::string("/log_*") ;
158✔
87
    system(command.c_str());
158✔
88
    return 0 ;
316✔
89
}
90

91
/**
92
@details
93
-# Initialize thread mutex and condition variable
94
-# Create a new thread calling the DataRecordThreaded Writer routine.
95
-# Wait for the data record thread to initialize before continuing.
96
*/
97
int Trick::DataRecordDispatcher::init() {
149✔
98

99
    pthread_mutex_lock(&drd_mutexes.init_complete_mutex);
149✔
100
    drd_writer_thread.create_thread() ;
149✔
101
    pthread_cond_wait(&drd_mutexes.init_complete_cv, &drd_mutexes.init_complete_mutex);
149✔
102
    pthread_mutex_unlock(&drd_mutexes.init_complete_mutex);
149✔
103

104
    return(0) ;
149✔
105
}
106

107
/**
108
add_sim_object is called by the executive when a new sim_object is added to the sim.
109
@details
110
-# Test if the incoming object is a DataRecordGroup.
111
 -# If it is add it to the list of groups we save
112
*/
113
int Trick::DataRecordDispatcher::add_sim_object(Trick::SimObject * in_object ) {
4,370✔
114
    Trick::DataRecordGroup * drg = dynamic_cast< Trick::DataRecordGroup * >(in_object) ;
4,370✔
115
    if ( drg != NULL ) {
4,370✔
116
        groups.push_back(drg) ;
50✔
117
    }
118
    return 0 ;
4,370✔
119
}
120

121
/** @brief set verification check on/off */
NEW
122
void Trick::DataRecordDispatcher::set_verif_onoff(bool on)
×
123
{
NEW
124
    verify_log_vars = on;
×
NEW
125
}
×
126

127
/**
128
@details
129
-# Test if the incoming data record group name has been used.
130
 -# If true return an error.
131
-# Set the group buffering type to the incoming type
132
-# Add the group to the executive.
133
*/
134
int Trick::DataRecordDispatcher::add_group(Trick::DataRecordGroup * in_group, Trick::DR_Buffering buffering) {
43✔
135

136
    unsigned int ii ;
137
    for (  ii = 0 ; ii < groups.size() ; ii++ ) {
66✔
138
        if ( !groups[ii]->group_name.compare(in_group->group_name) ) {
23✔
139
            message_publish(MSG_ERROR, "Data Record group with duplicate name %s.\n", in_group->group_name.c_str()) ;
×
140
            return -1 ;
×
141
        }
142
    }
143
    if ( buffering != Trick::DR_Not_Specified ) {
43✔
144
        in_group->set_buffer_type(buffering) ;
42✔
145
    }
146

147
    exec_add_sim_object(in_group , (char *)in_group->name.c_str()) ;
43✔
148

149
    return 0 ;
43✔
150
}
151

152

153
/**
154
@details
155
-# Remove the data recording group from the dispatcher's list of groups
156
-# Remove the group from the executive.
157
*/
158
int Trick::DataRecordDispatcher::remove_group(Trick::DataRecordGroup * in_group) {
27✔
159

160
    std::vector <Trick::DataRecordGroup *>::iterator drg_it ;
27✔
161

162

163
    // remove the group from the dispatcher vector of jobs.
164
    for ( drg_it = groups.begin() ; drg_it != groups.end() ; ) {
48✔
165
        if ( (*drg_it) == in_group ) {
21✔
166
            // erase the group from the dispatcher. Lock the mutex so we aren't in the
167
            // middle of writing data as we delete the group.
168
            pthread_mutex_lock(&drd_mutexes.dr_go_mutex) ;
×
169
            drg_it = groups.erase(drg_it) ;
×
170
            pthread_mutex_unlock(&drd_mutexes.dr_go_mutex) ;
×
171

172
            // call exec_remove_sim_object to remove the data recording jobs from the sim.
173
            exec_remove_sim_object(in_group) ;
×
174

175
            // shutdown recording for the group
176
            in_group->shutdown() ;
×
177
        } else {
178
            drg_it++ ;
21✔
179
        }
180
    }
181

182

183

184
    return 0 ;
27✔
185
}
186

187
void Trick::DataRecordDispatcher::remove_all_groups() {
×
188
    while (!groups.empty()) {
×
189
        remove_group(groups[0]);
×
190
    }
191
}
×
192

193
/**
194
 @details
195
 -# Gets the data recording group by its name
196
 */
197
Trick::DataRecordGroup * Trick::DataRecordDispatcher::get_group(std::string in_name) {
6✔
198
    std::vector <Trick::DataRecordGroup *>::iterator it ;
6✔
199
    for ( it = groups.begin() ; it != groups.end() ; ++it ) {
17✔
200
        if ( ! (*it)->get_group_name().compare(in_name) )
15✔
201
            return *it ;
4✔
202
    }
203
    return NULL ;
2✔
204
}
205

206
/**
207
 @details
208
 -# Gets the data recording group by its id number
209
 */
210
Trick::DataRecordGroup * Trick::DataRecordDispatcher::get_group(int in_idx) {
6✔
211
    if (!groups.empty() && in_idx > -1 && in_idx < groups.size()) {
6✔
212
        return groups[in_idx];
4✔
213
    }
214
    return NULL ;
2✔
215
}
216

217
/**
218
 @details
219
 -# Gets the size of all added data recroding groups
220
 */
221
int Trick::DataRecordDispatcher::get_groups_size() {
2✔
222
    return groups.size();
2✔
223
}
224

225
/**
226
@details
227
-# If the writer thread condition variable is unlocked
228
   -# Signal the thread to go
229
*/
230
int Trick::DataRecordDispatcher::signal_thread() {
106,232✔
231

232
    if (!pthread_mutex_trylock(&drd_mutexes.dr_go_mutex)) {
106,232✔
233
        pthread_cond_signal(&drd_mutexes.dr_go_cv);
94,204✔
234
        pthread_mutex_unlock(&drd_mutexes.dr_go_mutex);
94,204✔
235
    }
236

237
    return(0) ;
106,232✔
238
}
239

240
/**
241
@details
242
-# Close out current data record groups
243
-# Clears the data record groups we are tracking.  The checkpoint reload will
244
   repopluate this list.
245
*/
246
int Trick::DataRecordDispatcher::preload_checkpoint() {
9✔
247
    unsigned int ii ;
248
    // close out current data record groups
249
    for ( ii = 0 ; ii < groups.size() ; ii++ ) {
16✔
250
        groups[ii]->shutdown() ;
7✔
251
    }
252
    groups.clear() ;
9✔
253
    return 0 ;
9✔
254
}
255

256
/**
257
@details
258
-# If we have not called init yet (pthread_id == 0), call init.
259
-# Delete existing log files.
260
*/
261
int Trick::DataRecordDispatcher::restart() {
9✔
262
    // if we restarted from scratch without calling init jobs, need to call init
263
    if ( drd_writer_thread.get_pthread_id() == 0 ) {
9✔
264
        init() ;
1✔
265
    }
266
    // remove the current log files
267
    remove_files() ;
9✔
268
    return 0 ;
9✔
269
}
270

271
/**
272
@details
273
-# If the thread was started,
274
   -# Wait for the thread to be available
275
   -# Cancel the thread
276
*/
277
int Trick::DataRecordDispatcher::shutdown() {
149✔
278

279
    if ( drd_writer_thread.get_pthread_id() != 0 ) {
149✔
280
        pthread_mutex_lock( &drd_mutexes.dr_go_mutex);
149✔
281
        // pthread_cancel( drd_writer_thread.get_pthread_id()) ;
282
        drd_mutexes.cancelled = true;
149✔
283
        pthread_cond_signal(&drd_mutexes.dr_go_cv);
149✔
284
        pthread_mutex_unlock( &drd_mutexes.dr_go_mutex);
149✔
285
    }
286

287
    return(0) ;
149✔
288
}
289

290
int Trick::DataRecordDispatcher::enable( const char * in_name ) {
×
291
    unsigned int ii ;
292
    for ( ii = 0 ; ii < groups.size() ; ii++ ) {
×
293
        if ( in_name == NULL or !groups[ii]->get_group_name().compare(in_name) )
×
294
            groups[ii]->enable() ;
×
295
    }
296
    return 0 ;
×
297
}
298

299
int Trick::DataRecordDispatcher::disable( const char * in_name ) {
×
300
    unsigned int ii ;
301
    for ( ii = 0 ; ii < groups.size() ; ii++ ) {
×
302
        if ( in_name == NULL or !groups[ii]->get_group_name().compare(in_name) )
×
303
            groups[ii]->disable() ;
×
304
    }
305
    return 0 ;
×
306
}
307

308
int Trick::DataRecordDispatcher::record_now_group( const char * in_name ) {
×
309
    unsigned int ii ;
310
    if ( in_name != NULL ) {
×
311
        for ( ii = 0 ; ii < groups.size() ; ii++ ) {
×
312
            if ( !groups[ii]->get_group_name().compare(in_name) )
×
313
                groups[ii]->data_record(exec_get_sim_time()) ;
×
314
        }
315
    }
316
    return 0 ;
×
317
}
318

319
int Trick::DataRecordDispatcher::set_group_max_file_size(const char * in_name, uint64_t bytes){
×
320
    unsigned int ii ;
321
    for ( ii = 0 ; ii < groups.size() ; ii++ ) {
×
322
        if ( in_name == NULL or !groups[ii]->get_group_name().compare(in_name) )
×
323
            groups[ii]->set_max_file_size(bytes) ;
×
324
    }
325
    return 0 ;
×
326
}
327

328
int Trick::DataRecordDispatcher::set_max_file_size(uint64_t bytes) {
×
329
    unsigned int ii ;
330
    for ( ii = 0 ; ii < groups.size() ; ii++ ) {
×
331
        groups[ii]->set_max_file_size(bytes) ;
×
332
    }
333
    return 0 ;
×
334
}
335

336
/**
337
@details
338
-# Call every group's init job - only needed when restoring checkpoint
339
-# during initialization, and the user is responsible for calling this.
340
*/
341
int Trick::DataRecordDispatcher::init_groups() {
×
342
    unsigned int ii ;
343
    for ( ii = 0 ; ii < groups.size() ; ii++ ) {
×
344
        groups[ii]->init() ;
×
345
    }
346

NEW
347
    if(verify_log_vars)
×
348
    {
NEW
349
        processMultipleVarLoggedCheck();
×
350
    }
351

352
    return 0 ;
×
353
}
354

355
/**
356
@details
357
 * Checks the data logging configuration in Trick against certain error or warning conditions.
358
 * Current conditions for errors are:
359
 * -  A variable logged in two different DataRecordGroup instances at a different rate
360
 */
NEW
361
void Trick::DataRecordDispatcher::processMultipleVarLoggedCheck()
×
362
{
NEW
363
    bool isLoggedMultipleTimes = false;
×
364

365
    // Check that a variable isn't logged multiple times in a log file.
NEW
366
    checkMultiVarSingleLogGroup(isLoggedMultipleTimes);
×
367

368
    // Check that a variable isn't logged in multiple log files.
NEW
369
    checkMultiVarMultiLogGroups(isLoggedMultipleTimes);
×
370

NEW
371
    if(isLoggedMultipleTimes)
×
372
    {
NEW
373
        std::stringstream ss;
×
NEW
374
        ss << "Invalid or unsafe logging configuration. Exiting..." << std::endl;
×
NEW
375
        exec_terminate_with_return(-1, __FILE__, __LINE__, ss.str().c_str());
×
376
    }
NEW
377
}
×
378

NEW
379
void Trick::DataRecordDispatcher::checkMultiVarSingleLogGroup(bool & isLoggedMultipleTimes)
×
380
{
381
    // Check that a variable isn't logged multiple times in a log file.
NEW
382
    for(size_t grpIdx = 0; grpIdx < groups.size(); ++grpIdx)
×
383
    {
NEW
384
        Trick::DataRecordGroup * drGroup = groups[grpIdx];
×
385
        // Start at index 1 to skip exec time.
NEW
386
        for(size_t varOneIdx = 1; varOneIdx < drGroup->rec_buffer.size(); ++varOneIdx)
×
387
        {
NEW
388
            for(size_t varTwoIdx = varOneIdx + 1; varTwoIdx < drGroup->rec_buffer.size(); ++varTwoIdx)
×
389
            {
NEW
390
                Trick::DataRecordBuffer * var1 = drGroup->rec_buffer[varOneIdx];
×
NEW
391
                Trick::DataRecordBuffer * var2 = drGroup->rec_buffer[varTwoIdx];
×
NEW
392
                const std::string & var1CmpStr = var1->alias.empty() ? var1->name : var1->alias;
×
NEW
393
                const std::string & var2CmpStr = var2->alias.empty() ? var2->name : var2->alias;
×
NEW
394
                if(var1CmpStr.compare(var2CmpStr) == 0)
×
395
                {
NEW
396
                    isLoggedMultipleTimes = true;
×
397
                    // ERROR: OUR EXPERIENCE HERE IS THIS SCREWS UP DR PRODUCTS ENOUGH TO CONSIDER THIS FATAL
NEW
398
                    std::stringstream ss;
×
NEW
399
                    ss << "The variable \"";
×
NEW
400
                    if(var1->alias.empty())
×
401
                    {
NEW
402
                        ss << var1->name << "\"";
×
403
                    }
404
                    else
405
                    {
NEW
406
                        ss << var1->name << "\" (logged as alias \"" << var1->alias << "\")";
×
407
                    }
NEW
408
                    ss << " is being logged twice in the data recording group \"" << drGroup->group_name << "\" ."
×
NEW
409
                       << std::endl;
×
NEW
410
                    message_publish(MSG_ERROR, ss.str().c_str());
×
411
                }
412
            }
413
        }
414
    }
NEW
415
}
×
416

NEW
417
void Trick::DataRecordDispatcher::checkMultiVarMultiLogGroups(bool & isLoggedMultipleTimes)
×
418
{
NEW
419
    for(size_t grpOneIdx = 0; grpOneIdx < groups.size(); ++grpOneIdx)
×
420
    {
NEW
421
        Trick::DataRecordGroup * drGroupOne = groups[grpOneIdx];
×
NEW
422
        for(auto & var1 : drGroupOne->rec_buffer)
×
423
        {
NEW
424
            if(var1->name.compare("sys.exec.out.time") == 0)
×
425
            {
NEW
426
                continue;
×
427
            }
NEW
428
            for(size_t grpTwoIdx = grpOneIdx + 1; grpTwoIdx < groups.size(); ++grpTwoIdx)
×
429
            {
NEW
430
                Trick::DataRecordGroup * drGroupTwo = groups[grpTwoIdx];
×
431

NEW
432
                auto nameCompare = [var1](Trick::DataRecordBuffer * var2)
×
433
                {
NEW
434
                    const std::string & var1CmpStr = var1->alias.empty() ? var1->name : var1->alias;
×
NEW
435
                    const std::string & var2CmpStr = var2->alias.empty() ? var2->name : var2->alias;
×
NEW
436
                    return (var1CmpStr.compare(var2CmpStr) == 0);
×
NEW
437
                };
×
438

NEW
439
                auto result = std::find_if(drGroupTwo->rec_buffer.begin(), drGroupTwo->rec_buffer.end(), nameCompare);
×
NEW
440
                while(result != drGroupTwo->rec_buffer.end())
×
441
                {
NEW
442
                    isLoggedMultipleTimes = true;
×
443
                    // ERROR: OUR EXPERIENCE HERE IS THIS SCREWS UP DR PRODUCTS ENOUGH TO CONSIDER THIS FATAL
NEW
444
                    std::stringstream ss;
×
NEW
445
                    ss << "The variable \"";
×
NEW
446
                    if(var1->alias.empty())
×
447
                    {
NEW
448
                        ss << var1->name << "\"";
×
449
                    }
450
                    else
451
                    {
NEW
452
                        ss << var1->name << "\" (logged as alias \"" << var1->alias << "\")";
×
453
                    }
NEW
454
                    ss << " is being logged in both \"" << drGroupOne->group_name << "\" and \""
×
NEW
455
                       << drGroupTwo->group_name << "\" data recording groups." << std::endl;
×
NEW
456
                    message_publish(MSG_ERROR, ss.str().c_str());
×
NEW
457
                    result = std::find_if(++result, drGroupTwo->rec_buffer.end(), nameCompare);
×
458
                }
459
            }
460
        }
461
    }
NEW
462
}
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc