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

nasa / trick / 18506050876

14 Oct 2025 06:17PM UTC coverage: 55.818% (-0.004%) from 55.822%
18506050876

Pull #1942

github

web-flow
Merge 922bf3ddc into 6ff28150d
Pull Request #1942: Adding additional updates to PR#1847

42 of 81 new or added lines in 2 files covered. (51.85%)

129 existing lines in 6 files now uncovered.

12401 of 22217 relevant lines covered (55.82%)

252363.25 hits per line

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

64.04
/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() {
182✔
29
    pthread_cond_init(&dr_go_cv, NULL);
182✔
30
    pthread_mutex_init(&dr_go_mutex, NULL);
182✔
31
    pthread_cond_init(&init_complete_cv, NULL);
182✔
32
    pthread_mutex_init(&init_complete_mutex, NULL);
182✔
33
    cancelled = false;
182✔
34
}
182✔
35

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

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

44
    /* tell the main thread that the writer is ready to go */
45
    pthread_mutex_lock(&(drd_mutexes.init_complete_mutex));
150✔
46
    pthread_cond_signal(&(drd_mutexes.init_complete_cv));
150✔
47
    pthread_mutex_unlock(&(drd_mutexes.init_complete_mutex));
150✔
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));
45,409✔
53
        if (drd_mutexes.cancelled) {
45,409✔
54
            pthread_mutex_unlock(&(drd_mutexes.dr_go_mutex));
150✔
55
            pthread_exit(0);
150✔
56
        }
57
        for ( unsigned int ii = 0 ; ii < groups.size() ; ii++ ) {
169,284✔
58
            if ( groups[ii]->buffer_type == Trick::DR_Buffer ) {
124,025✔
59
                groups[ii]->write_data(true) ;
43,801✔
60
            }
61
        }
62
    }
45,259✔
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()
182✔
74
    : drd_writer_thread(drd_mutexes, groups),
182✔
75
      verify_log_vars(true),
76
      warning_level(0)
182✔
77
{
78
    the_drd = this;
182✔
79
}
182✔
80

81
Trick::DataRecordDispatcher::~DataRecordDispatcher() {
182✔
82
}
182✔
83

84
int Trick::DataRecordDispatcher::remove_files() {
159✔
85

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

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

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

105
    if(verify_log_vars)
150✔
106
    {
107
        processMultipleVarLoggedCheck();
150✔
108
    }
109

110
    return(0) ;
150✔
111
}
112

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

127
/** @brief set verification check on/off */
NEW
128
void Trick::DataRecordDispatcher::set_verif_onoff(bool state)
×
129
{
NEW
130
    verify_log_vars = state;
×
NEW
131
}
×
132

133
/** @brief set verification warning level */
NEW
134
void Trick::DataRecordDispatcher::set_warning_level(int level)
×
135
{
NEW
136
    warning_level = level;
×
NEW
137
}
×
138

139
/**
140
@details
141
-# Test if the incoming data record group name has been used.
142
 -# If true return an error.
143
-# Set the group buffering type to the incoming type
144
-# Add the group to the executive.
145
*/
146
int Trick::DataRecordDispatcher::add_group(Trick::DataRecordGroup * in_group, Trick::DR_Buffering buffering) {
43✔
147

148
    unsigned int ii ;
149
    for (  ii = 0 ; ii < groups.size() ; ii++ ) {
66✔
150
        if ( !groups[ii]->group_name.compare(in_group->group_name) ) {
23✔
151
            message_publish(MSG_ERROR, "Data Record group with duplicate name %s.\n", in_group->group_name.c_str()) ;
×
152
            return -1 ;
×
153
        }
154
    }
155
    if ( buffering != Trick::DR_Not_Specified ) {
43✔
156
        in_group->set_buffer_type(buffering) ;
42✔
157
    }
158

159
    exec_add_sim_object(in_group , (char *)in_group->name.c_str()) ;
43✔
160

161
    return 0 ;
43✔
162
}
163

164

165
/**
166
@details
167
-# Remove the data recording group from the dispatcher's list of groups
168
-# Remove the group from the executive.
169
*/
170
int Trick::DataRecordDispatcher::remove_group(Trick::DataRecordGroup * in_group) {
27✔
171

172
    std::vector <Trick::DataRecordGroup *>::iterator drg_it ;
27✔
173

174

175
    // remove the group from the dispatcher vector of jobs.
176
    for ( drg_it = groups.begin() ; drg_it != groups.end() ; ) {
48✔
177
        if ( (*drg_it) == in_group ) {
21✔
178
            // erase the group from the dispatcher. Lock the mutex so we aren't in the
179
            // middle of writing data as we delete the group.
180
            pthread_mutex_lock(&drd_mutexes.dr_go_mutex) ;
×
181
            drg_it = groups.erase(drg_it) ;
×
182
            pthread_mutex_unlock(&drd_mutexes.dr_go_mutex) ;
×
183

184
            // call exec_remove_sim_object to remove the data recording jobs from the sim.
185
            exec_remove_sim_object(in_group) ;
×
186

187
            // shutdown recording for the group
188
            in_group->shutdown() ;
×
189
        } else {
190
            drg_it++ ;
21✔
191
        }
192
    }
193

194

195

196
    return 0 ;
27✔
197
}
198

199
void Trick::DataRecordDispatcher::remove_all_groups() {
×
200
    while (!groups.empty()) {
×
201
        remove_group(groups[0]);
×
202
    }
203
}
×
204

205
/**
206
 @details
207
 -# Gets the data recording group by its name
208
 */
209
Trick::DataRecordGroup * Trick::DataRecordDispatcher::get_group(std::string in_name) {
6✔
210
    std::vector <Trick::DataRecordGroup *>::iterator it ;
6✔
211
    for ( it = groups.begin() ; it != groups.end() ; ++it ) {
17✔
212
        if ( ! (*it)->get_group_name().compare(in_name) )
15✔
213
            return *it ;
4✔
214
    }
215
    return NULL ;
2✔
216
}
217

218
/**
219
 @details
220
 -# Gets the data recording group by its id number
221
 */
222
Trick::DataRecordGroup * Trick::DataRecordDispatcher::get_group(int in_idx) {
6✔
223
    if (!groups.empty() && in_idx > -1 && in_idx < groups.size()) {
6✔
224
        return groups[in_idx];
4✔
225
    }
226
    return NULL ;
2✔
227
}
228

229
/**
230
 @details
231
 -# Gets the size of all added data recroding groups
232
 */
233
int Trick::DataRecordDispatcher::get_groups_size() {
2✔
234
    return groups.size();
2✔
235
}
236

237
/**
238
@details
239
-# If the writer thread condition variable is unlocked
240
   -# Signal the thread to go
241
*/
242
int Trick::DataRecordDispatcher::signal_thread() {
106,242✔
243

244
    if (!pthread_mutex_trylock(&drd_mutexes.dr_go_mutex)) {
106,242✔
245
        pthread_cond_signal(&drd_mutexes.dr_go_cv);
87,406✔
246
        pthread_mutex_unlock(&drd_mutexes.dr_go_mutex);
87,406✔
247
    }
248

249
    return(0) ;
106,242✔
250
}
251

252
/**
253
@details
254
-# Close out current data record groups
255
-# Clears the data record groups we are tracking.  The checkpoint reload will
256
   repopluate this list.
257
*/
258
int Trick::DataRecordDispatcher::preload_checkpoint() {
9✔
259
    unsigned int ii ;
260
    // close out current data record groups
261
    for ( ii = 0 ; ii < groups.size() ; ii++ ) {
16✔
262
        groups[ii]->shutdown() ;
7✔
263
    }
264
    groups.clear() ;
9✔
265
    return 0 ;
9✔
266
}
267

268
/**
269
@details
270
-# If we have not called init yet (pthread_id == 0), call init.
271
-# Delete existing log files.
272
*/
273
int Trick::DataRecordDispatcher::restart() {
9✔
274
    // if we restarted from scratch without calling init jobs, need to call init
275
    if ( drd_writer_thread.get_pthread_id() == 0 ) {
9✔
276
        init() ;
1✔
277
    }
278
    // remove the current log files
279
    remove_files() ;
9✔
280
    return 0 ;
9✔
281
}
282

283
/**
284
@details
285
-# If the thread was started,
286
   -# Wait for the thread to be available
287
   -# Cancel the thread
288
*/
289
int Trick::DataRecordDispatcher::shutdown() {
150✔
290

291
    if ( drd_writer_thread.get_pthread_id() != 0 ) {
150✔
292
        pthread_mutex_lock( &drd_mutexes.dr_go_mutex);
150✔
293
        // pthread_cancel( drd_writer_thread.get_pthread_id()) ;
294
        drd_mutexes.cancelled = true;
150✔
295
        pthread_cond_signal(&drd_mutexes.dr_go_cv);
150✔
296
        pthread_mutex_unlock( &drd_mutexes.dr_go_mutex);
150✔
297
    }
298

299
    return(0) ;
150✔
300
}
301

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

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

320
int Trick::DataRecordDispatcher::record_now_group( const char * in_name ) {
×
321
    unsigned int ii ;
322
    if ( in_name != NULL ) {
×
323
        for ( ii = 0 ; ii < groups.size() ; ii++ ) {
×
324
            if ( !groups[ii]->get_group_name().compare(in_name) )
×
325
                groups[ii]->data_record(exec_get_sim_time()) ;
×
326
        }
327
    }
328
    return 0 ;
×
329
}
330

331
int Trick::DataRecordDispatcher::set_group_max_file_size(const char * in_name, uint64_t bytes){
×
332
    unsigned int ii ;
333
    for ( ii = 0 ; ii < groups.size() ; ii++ ) {
×
334
        if ( in_name == NULL or !groups[ii]->get_group_name().compare(in_name) )
×
335
            groups[ii]->set_max_file_size(bytes) ;
×
336
    }
337
    return 0 ;
×
338
}
339

340
int Trick::DataRecordDispatcher::set_max_file_size(uint64_t bytes) {
×
341
    unsigned int ii ;
342
    for ( ii = 0 ; ii < groups.size() ; ii++ ) {
×
343
        groups[ii]->set_max_file_size(bytes) ;
×
344
    }
345
    return 0 ;
×
346
}
347

348
/**
349
@details
350
-# Call every group's init job - only needed when restoring checkpoint
351
-# during initialization, and the user is responsible for calling this.
352
*/
353
int Trick::DataRecordDispatcher::init_groups() {
×
354
    unsigned int ii ;
355
    for ( ii = 0 ; ii < groups.size() ; ii++ ) {
×
356
        groups[ii]->init() ;
×
357
    }
358

UNCOV
359
    return 0 ;
×
360
}
361

362
/**
363
@details
364
 * Checks the data logging configuration in Trick against certain error or warning conditions.
365
 * Current conditions for errors are:
366
 * -  A variable logged in two different DataRecordGroup instances at a different rate
367
 */
368
void Trick::DataRecordDispatcher::processMultipleVarLoggedCheck()
150✔
369
{
370
    bool isLoggedMultipleTimes = false;
150✔
371

372
    // Check that a variable isn't logged multiple times in a log file.
373
    checkMultiVarSingleLogGroup(isLoggedMultipleTimes);
150✔
374

375
    // Check that a variable isn't logged in multiple log files.
376
    checkMultiVarMultiLogGroups(isLoggedMultipleTimes);
150✔
377

378
    if(isLoggedMultipleTimes && warning_level > 0)
150✔
379
    {
NEW
380
        std::stringstream ss;
×
NEW
381
        ss << "Invalid or unsafe logging configuration. Exiting..." << std::endl;
×
NEW
382
        if (warning_level >= 2)
×
NEW
383
            exec_terminate_with_return(-1, __FILE__, __LINE__, ss.str().c_str());
×
384
    }
385
}
150✔
386

387
void Trick::DataRecordDispatcher::checkMultiVarSingleLogGroup(bool & isLoggedMultipleTimes)
150✔
388
{
389
    // Check that a variable isn't logged multiple times in a log file.
390
    for(size_t grpIdx = 0; grpIdx < groups.size(); ++grpIdx)
192✔
391
    {
392
        Trick::DataRecordGroup * drGroup = groups[grpIdx];
42✔
393
        // Start at index 1 to skip exec time.
394
        for(size_t varOneIdx = 1; varOneIdx < drGroup->rec_buffer.size(); ++varOneIdx)
587✔
395
        {
396
            for(size_t varTwoIdx = varOneIdx + 1; varTwoIdx < drGroup->rec_buffer.size(); ++varTwoIdx)
10,570✔
397
            {
398
                Trick::DataRecordBuffer * var1 = drGroup->rec_buffer[varOneIdx];
10,025✔
399
                Trick::DataRecordBuffer * var2 = drGroup->rec_buffer[varTwoIdx];
10,025✔
400
                const std::string & var1CmpStr = var1->alias.empty() ? var1->name : var1->alias;
10,025✔
401
                const std::string & var2CmpStr = var2->alias.empty() ? var2->name : var2->alias;
10,025✔
402
                if(var1CmpStr.compare(var2CmpStr) == 0)
10,025✔
403
                {
NEW
404
                    isLoggedMultipleTimes = true;
×
NEW
405
                    if (warning_level >= 1)
×
406
                    {
NEW
407
                        std::stringstream ss;
×
NEW
408
                        ss << "The variable \"";
×
NEW
409
                        if(var1->alias.empty())
×
410
                        {
NEW
411
                            ss << var1->name << "\"";
×
412
                        }
413
                        else
414
                        {
NEW
415
                            ss << var1->name << "\" (logged as alias \"" << var1->alias << "\")";
×
416
                        }
NEW
417
                        ss << " is being logged twice in the data recording group \"" << drGroup->group_name << "\" ."
×
NEW
418
                            << std::endl;
×
NEW
419
                        message_publish(MSG_ERROR, ss.str().c_str());
×
420
                    }
421
                }
422
            }
423
        }
424
    }
425
}
150✔
426

427
void Trick::DataRecordDispatcher::checkMultiVarMultiLogGroups(bool & isLoggedMultipleTimes)
150✔
428
{
429
    for(size_t grpOneIdx = 0; grpOneIdx < groups.size(); ++grpOneIdx)
192✔
430
    {
431
        Trick::DataRecordGroup * drGroupOne = groups[grpOneIdx];
42✔
432
        for(auto & var1 : drGroupOne->rec_buffer)
629✔
433
        {
434
            if(var1->name.compare("sys.exec.out.time") == 0)
587✔
435
            {
436
                continue;
42✔
437
            }
438
            for(size_t grpTwoIdx = grpOneIdx + 1; grpTwoIdx < groups.size(); ++grpTwoIdx)
1,103✔
439
            {
440
                Trick::DataRecordGroup * drGroupTwo = groups[grpTwoIdx];
558✔
441

442
                auto nameCompare = [var1](Trick::DataRecordBuffer * var2)
51,158✔
443
                {
444
                    const std::string & var1CmpStr = var1->alias.empty() ? var1->name : var1->alias;
25,579✔
445
                    const std::string & var2CmpStr = var2->alias.empty() ? var2->name : var2->alias;
25,579✔
446
                    return (var1CmpStr.compare(var2CmpStr) == 0);
25,579✔
447
                };
558✔
448

449
                auto result = std::find_if(drGroupTwo->rec_buffer.begin(), drGroupTwo->rec_buffer.end(), nameCompare);
558✔
450
                while(result != drGroupTwo->rec_buffer.end())
614✔
451
                {
452
                    isLoggedMultipleTimes = true;
56✔
453
                    if (warning_level >= 1)
56✔
454
                    {
NEW
455
                        std::stringstream ss;
×
NEW
456
                        ss << "The variable \"";
×
NEW
457
                        if(var1->alias.empty())
×
458
                        {
NEW
459
                            ss << var1->name << "\"";
×
460
                        }
461
                        else
462
                        {
NEW
463
                            ss << var1->name << "\" (logged as alias \"" << var1->alias << "\")";
×
464
                        }
NEW
465
                        ss << " is being logged in both \"" << drGroupOne->group_name << "\" and \""
×
NEW
466
                            << drGroupTwo->group_name << "\" data recording groups." << std::endl;
×
NEW
467
                        message_publish(MSG_ERROR, ss.str().c_str());
×
468
                    }
469
                    result = std::find_if(++result, drGroupTwo->rec_buffer.end(), nameCompare);
56✔
470
                }
471
            }
472
        }
473
    }
474
}
150✔
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