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

nasa / trick / 22071109586

16 Feb 2026 04:49PM UTC coverage: 55.753% (+0.1%) from 55.624%
22071109586

Pull #2016

github

web-flow
Merge 12e8524d4 into 764b4b429
Pull Request #2016: better support set_cycle in drgroup

100 of 124 new or added lines in 6 files covered. (80.65%)

48 existing lines in 4 files now uncovered.

12593 of 22587 relevant lines covered (55.75%)

316136.93 hits per line

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

83.71
/trick_source/sim_services/DataRecord/DataRecordGroup.cpp
1

2
#include <algorithm>
3
#include <string>
4
#include <iostream>
5
#include <sstream>
6
#include <string.h>
7
#include <stdlib.h>
8
#include <iomanip>
9
#include <math.h>
10

11
#ifdef __GNUC__
12
#include <cxxabi.h>
13
#endif
14

15
#include "trick/DataRecordGroup.hh"
16
#include "trick/command_line_protos.h"
17
#include "trick/exec_proto.h"
18
#include "trick/reference.h"
19
#include "trick/memorymanager_c_intf.h"
20
#include "trick/message_proto.h"
21
#include "trick/message_type.h"
22

23

24
const double Trick::DataRecordGroup::default_cyle = 0.1;
25

26
/**
27
@details
28
-# The recording group is enabled
29
-# The recording group is set to always record data (not changes only)
30
-# The recording group is set to allocate a maximum of 100,000 records in memory
31
-# The recording group is set to record at a cycle period of 0.1 seconds.
32
-# The first recording parameter is assigned to simulation time.  The name of
33
   the parmeter is named "sys.exec.out.time" to preserve backwards compatibility
34
   with older %Trick releases.
35
-# The call_function and call_function_double functions inherited from the SimObject
36
   are populated.  The data_record function is added as a job for this SimObject.
37

38
A sim object is created named @c data_record_group<n> where <n> is a unique group number. A
39
data_record class job within this sim object is also created that will write the group's
40
recorded data to disk. The job's name is <tt> data_record_group<n>.<in_name></tt>.
41

42
*/
43
Trick::DataRecordBuffer::DataRecordBuffer() {
5,692✔
44
    buffer = last_value = NULL ;
5,692✔
45
    ref = NULL ;
5,692✔
46
    ref_searched = false ;
5,692✔
47
}
5,692✔
48

49
Trick::DataRecordBuffer::~DataRecordBuffer() {
636✔
50
    if ( buffer ) {
636✔
51
        free(buffer) ;
621✔
52
    }
53
    if ( last_value ) {
636✔
54
        free(last_value) ;
624✔
55
    }
56

57
    ref_free(ref) ;
636✔
58
    free(ref) ;
636✔
59
}
636✔
60

61
Trick::LoggingCycle::LoggingCycle(double rate_in)
622✔
62
{
63
    set_rate(rate_in);
622✔
64
}
622✔
65

66
void Trick::LoggingCycle::set_rate(double rate_in)
683✔
67
{
68
    rate_in_seconds = rate_in;
683✔
69
    rate_in_tics = (long long)round(rate_in * Trick::JobData::time_tic_value);
683✔
70
}
683✔
71

72
long long Trick::LoggingCycle::calc_next_tics_on_or_after_input_tic(long long input_tic, long long cycle_tic)
22,035,236✔
73
{
74
    long long next_tic;
75
    if((input_tic % cycle_tic) != 0)
22,035,236✔
76
    {
77
        next_tic = (input_tic/cycle_tic) * cycle_tic + cycle_tic;
15,115,132✔
78
    } else 
79
    {
80
        next_tic = input_tic;
6,920,104✔
81
    }
82
    return next_tic;
22,035,236✔
83
}
84

NEW
85
Trick::DataRecordGroupJobData::DataRecordGroupJobData(Trick::DataRecordGroup &owner_in)
×
NEW
86
    : owner(owner_in)
×
87
{
NEW
88
}
×
89

90
Trick::DataRecordGroupJobData::DataRecordGroupJobData(Trick::DataRecordGroup &owner_in, int in_thread, int in_id, std::string in_job_class_name, void *in_sup_class_data,
616✔
91
                                                      double in_cycle, std::string in_name, std::string in_tag, int in_phase,
92
                                                      double in_start, double in_stop)
616✔
93
    : Trick::JobData(in_thread, in_id, in_job_class_name, in_sup_class_data, in_cycle, in_name, in_tag, in_phase, in_start, in_stop),
94
      owner(owner_in)
616✔
95
{
96
}
616✔
97

98
int Trick::DataRecordGroupJobData::set_cycle(double rate)
1✔
99
{
100
    owner.set_cycle(rate);
1✔
101
    return 0;
1✔
102
}
103

104
void Trick::DataRecordGroupJobData::enable()
2✔
105
{
106
    if(disabled)
2✔
107
    {
108
        Trick::JobData::enable();    
2✔
109
        owner.set_cycle(owner.get_rate());
2✔
110
    }
111
}
2✔
112

113
Trick::DataRecordGroup::DataRecordGroup( std::string in_name, Trick::DR_Type dr_type ) :
616✔
114
 record(true) ,
115
 inited(false) ,
116
 group_name(in_name) ,
117
 freq(DR_Always),
118
 start(0.0),
119
 time_value_attr() ,
120
 num_variable_names(0),
121
 variable_names(NULL),
122
 variable_alias(NULL),
123
 num_change_variable_names(0),
124
 change_variable_names(NULL),
125
 change_variable_alias(NULL),
126
 max_num(100000),
127
 buffer_num(0),
128
 writer_num(0),
129
 max_file_size(1<<30), // 1 GB
130
 total_bytes_written(0),
131
 max_size_warning(false),
132
 writer_buff(NULL),
133
 single_prec_only(false),
134
 buffer_type(DR_Buffer),
135
 job_class("data_record"),
136
 curr_time(0.0),
137
 curr_time_dr_job(0.0)
616✔
138
{
139

140
    union {
141
        long l;
142
        char c[sizeof(long)];
143
    } byte_order_union;
144

145
    byte_order_union.l = 1;
616✔
146
    if (byte_order_union.c[sizeof(long) - 1] != 1) {
616✔
147
        byte_order = "little_endian" ;
616✔
148
    } else {
149
        byte_order = "big_endian" ;
×
150
    }
151

152
    // sim object name
153
    name = std::string("trick_data_record_group_") + in_name ;
616✔
154

155
    configure_jobs(dr_type) ;
616✔
156

157
    add_time_variable() ;
616✔
158

159
    logging_rates.emplace_back(default_cyle);
616✔
160
}
616✔
161

162
Trick::DataRecordGroup::~DataRecordGroup() {
44✔
163
    free((void *)time_value_attr.units) ;
44✔
164
}
44✔
165

166
int Trick::DataRecordGroup::call_function( Trick::JobData * curr_job ) {
5,127,059✔
167

168
    int ret = 0 ;
5,127,059✔
169

170
    switch (curr_job->id) {
5,127,059✔
171
        case 1:
46✔
172
            ret = init() ;
46✔
173
            break ;
46✔
174
        case 2:
372,270✔
175
            ret = write_data(false) ;
372,270✔
176
            break ;
372,270✔
177
        case 3:
13✔
178
            ret = checkpoint() ;
13✔
179
            break ;
13✔
180
        case 4:
13✔
181
            clear_checkpoint_vars() ;
13✔
182
            break ;
13✔
183
        case 5:
9✔
184
            ret = restart() ;
9✔
185
            break ;
9✔
186
        case 6:
46✔
187
            ret = shutdown() ;
46✔
188
            break ;
46✔
189
        default:
4,754,662✔
190
            ret = data_record(exec_get_sim_time()) ;
4,754,662✔
191
            break ;
4,754,662✔
192
    }
193

194
    return(ret) ;
5,127,059✔
195

196
}
197

198
double Trick::DataRecordGroup::call_function_double( Trick::JobData * curr_job ) {
×
199
    (void) curr_job ;
200
    return(0.0) ;
×
201
}
202

203
void Trick::DataRecordGroup::register_group_with_mm(void * address , const char * type) {
44✔
204
    // Only add to the memory manager if it has not already been added
205
    if ( TMM_var_exists(name.c_str()) == 0 ) {
44✔
206
        // Declare this to the memory manager.  Must be done here to get the correct type name
207
        TMM_declare_ext_var(address , TRICK_STRUCTURED, type , 0 , name.c_str() , 0 , NULL ) ;
44✔
208
        ALLOC_INFO * alloc_info = get_alloc_info_at(address) ;
44✔
209
        alloc_info->stcl = TRICK_LOCAL ;
44✔
210
        alloc_info->alloc_type = TRICK_ALLOC_NEW ;
44✔
211
    }
212
}
44✔
213

214
const std::string & Trick::DataRecordGroup::get_group_name() {
45✔
215
    return group_name ;
45✔
216
}
217

NEW
218
size_t Trick::DataRecordGroup::get_num_rates(){
×
NEW
219
    return logging_rates.size();
×
220
}
221

222
double Trick::DataRecordGroup::get_rate(const size_t rateIdx)
2✔
223
{
224
    if(rateIdx < logging_rates.size())
2✔
225
    {
226
        return logging_rates[rateIdx].rate_in_seconds;
2✔
227
    }
NEW
228
    return -1.0;
×
229
}
230

231
int Trick::DataRecordGroup::set_cycle( double in_cycle ) {
54✔
232
    return set_rate(0, in_cycle);    
54✔
233
}
234

235
int Trick::DataRecordGroup::add_cycle(double in_cycle)
6✔
236
{
237
    logging_rates.emplace_back(in_cycle);
6✔
238
    set_rate(logging_rates.size()-1, in_cycle);
6✔
239
    return (int)logging_rates.size()-1;
6✔
240
}
241

242
int Trick::DataRecordGroup::set_rate(const size_t rate_idx, const double rate_in)
64✔
243
{
244
    if(rate_idx >= logging_rates.size())
64✔
245
    {
NEW
246
        message_publish(MSG_ERROR, "DataRecordGroup ERROR: DR Group \"%s\" : set_rate: invalid rate idx %lu\n", group_name.c_str(), rate_idx);
×
NEW
247
        return 1;
×
248
    }
249
    if(inited) {
64✔
250
        int ret = check_if_rate_is_valid(rate_in);
14✔
251
        if(ret)
14✔
252
        {
253
            emit_rate_error(ret, rate_idx, rate_in);
3✔
254
            message_publish(MSG_ERROR, "DataRecordGroup ERROR: DR Group \"%s\" : Rejecting runtime set_rate(%lu, %.16g)\n", group_name.c_str(), rate_idx, rate_in);
3✔
255
            return 1;
3✔
256
        }
257
        long long prev_log_tics = (long long)round(curr_time_dr_job * Trick::JobData::time_tic_value);
11✔
258
        long long curr_time_tic = exec_get_time_tics();
11✔
259
        LoggingCycle & curLog = logging_rates[rate_idx];
11✔
260
        curLog.set_rate(rate_in);
11✔
261
        curLog.next_cycle_in_tics = LoggingCycle::calc_next_tics_on_or_after_input_tic(curr_time_tic, curLog.rate_in_tics);
11✔
262
        if(curLog.next_cycle_in_tics == curr_time_tic && curLog.next_cycle_in_tics == prev_log_tics)
11✔
263
        {
NEW
264
            curLog.next_cycle_in_tics += curLog.rate_in_tics;
×
265
        }
266
        for(auto & logCycle : logging_rates)
33✔
267
        {
268
            logCycle.next_cycle_in_tics = 0;
22✔
269
        }
270
        advance_log_tics_given_curr_tic(curr_time_tic-1);
11✔
271
        write_job->next_tics = calculate_next_logging_tic(curr_time_tic-1);
11✔
272

273
        long long next_next_tics = calculate_next_logging_tic(write_job->next_tics);
11✔
274
        write_job->cycle_tics = next_next_tics - write_job->next_tics;
11✔
275
        write_job->cycle = (double)write_job->cycle_tics / Trick::JobData::time_tic_value;
11✔
276
    } else {
277
        logging_rates[rate_idx].set_rate(rate_in);
50✔
278
    }
279
    return 0;
61✔
280
}
281

282
int Trick::DataRecordGroup::set_phase( unsigned short in_phase ) {
596✔
283
    write_job->phase = in_phase ;
596✔
284
    return(0) ;
596✔
285
}
286

287
int Trick::DataRecordGroup::set_freq( DR_Freq in_freq ) {
38✔
288
    freq = in_freq ;
38✔
289
    return(0) ;
38✔
290
}
291

292
int Trick::DataRecordGroup::set_max_buffer_size( int num ) {
×
293
    max_num = num ;
×
294
    return(0) ;
×
295
}
296

297
int Trick::DataRecordGroup::set_buffer_type( int in_buffer_type ) {
46✔
298
    buffer_type = (DR_Buffering)in_buffer_type ;
46✔
299
    return(0) ;
46✔
300
}
301

302
int Trick::DataRecordGroup::set_max_file_size( uint64_t bytes ) {
18✔
303
    if(bytes == 0) {
18✔
304
        max_file_size = UINT64_MAX ;
×
305
    } else {
306
    max_file_size = bytes ; 
18✔
307
    }
308
    return(0) ;
18✔
309
}
310

311
int Trick::DataRecordGroup::set_single_prec_only( bool in_single_prec_only ) {
26✔
312
    single_prec_only = in_single_prec_only ;
26✔
313
    return(0) ;
26✔
314
}
315

316
int Trick::DataRecordGroup::set_thread( unsigned int in_thread_id ) {
202✔
317

318
    unsigned int jj ;
319
    Trick::JobData * temp_job  ;
320

321
    /* make all data_record_groups have same sim object id as data_record */
322
    for ( jj = 0 ; jj < jobs.size() ; jj++ ) {
1,828✔
323
        temp_job = jobs[jj] ;
1,626✔
324
        temp_job->thread = in_thread_id ;
1,626✔
325
    }
326
    return 0 ;
202✔
327
}
328

329
int Trick::DataRecordGroup::set_job_class( std::string in_class ) {
596✔
330
    write_job->job_class_name = job_class = in_class ;
596✔
331
    return(0) ;
596✔
332
}
333

334
int Trick::DataRecordGroup::add_time_variable() {
658✔
335
    REF2 * new_ref ;
336

337
    // Create attributes for time recorded as a double
338
    time_value_attr.type = TRICK_DOUBLE ;
658✔
339
    time_value_attr.size = sizeof(double) ;
658✔
340
    time_value_attr.units = strdup("s") ;
658✔
341

342
    // Create a reference that records time as sys.exec.out.time
343
    new_ref = (REF2 *)calloc( 1 , sizeof(REF2));
658✔
344
    new_ref->reference = strdup("sys.exec.out.time") ;
658✔
345
    new_ref->address = &curr_time ;
658✔
346
    new_ref->attr = &time_value_attr ;
658✔
347
    add_variable(new_ref) ;
658✔
348

349
    return 0 ;
658✔
350
}
351

352
int Trick::DataRecordGroup::add_variable( std::string in_name , std::string alias ) {
964✔
353

354
    Trick::DataRecordBuffer * new_var = new Trick::DataRecordBuffer ;
964✔
355
    // Trim leading spaces
356
    in_name.erase( 0, in_name.find_first_not_of( " \t" ) );
964✔
357
    // Trim trailing spaces
358
    in_name.erase( in_name.find_last_not_of( " \t" ) + 1);
964✔
359
    new_var->name = in_name ;
964✔
360
    new_var->alias = alias ;
964✔
361
    rec_buffer.push_back(new_var) ;
964✔
362
    return 0 ;
964✔
363
}
364

365
void Trick::DataRecordGroup::remove_variable( std::string in_name ) {
×
366
    // Trim leading spaces++ 
367
    in_name.erase( 0, in_name.find_first_not_of( " \t" ) );
×
368
    // Trim trailing spaces
369
    in_name.erase( in_name.find_last_not_of( " \t" ) + 1);
×
370

371
    if (!in_name.compare("sys.exec.out.time")) {
×
372
        // This class assumes sim time is always the first variable.
373
        // Removing it results in errors.
374
        return;
×
375
    }
376

377
    auto remove_from = [&](std::vector<DataRecordBuffer*>& buffer) {
×
378
        for (auto i = buffer.begin(); i != buffer.end(); ++i) {
×
379
            if (!(*i)->name.compare(in_name)) {
×
380
                delete *i;
×
381
                buffer.erase(i);
×
382
                break;
×
383
            }
384
        }
385
    };
×
386

387
    remove_from(rec_buffer);
×
388
    remove_from(change_buffer);
×
389
}
390

391
void Trick::DataRecordGroup::remove_all_variables() {
55✔
392
    // remove all but the first variable, which is sim time
393
    if(!rec_buffer.empty()) {
55✔
394
        for (auto i = rec_buffer.begin() + 1; i != rec_buffer.end(); ++i) {
615✔
395
            delete *i;
560✔
396
        }
397
        rec_buffer.erase(rec_buffer.begin() + 1, rec_buffer.end());
55✔
398
    }
399

400
    // remove everything
401
    for (auto variable : change_buffer) {
61✔
402
        delete variable;
6✔
403
    }
404

405
    change_buffer.clear();
55✔
406
}
55✔
407

408
int Trick::DataRecordGroup::add_variable( REF2 * ref2 ) {
4,722✔
409

410
    if ( ref2 == NULL || ref2->attr == NULL ) {
4,722✔
411
        return(-1) ;
×
412
    }
413

414
    Trick::DataRecordBuffer * new_var = new Trick::DataRecordBuffer ;
4,722✔
415
    new_var->name = std::string(ref2->reference) ;
4,722✔
416
    new_var->ref_searched = true ;
4,722✔
417
    new_var->ref = ref2 ;
4,722✔
418
    new_var->last_value = (char *)calloc(1 , new_var->ref->attr->size) ;
4,722✔
419
    // Don't allocate space for the temp storage buffer until "init"
420
    rec_buffer.push_back(new_var) ;
4,722✔
421

422
    return(0) ;
4,722✔
423

424
}
425

426
int Trick::DataRecordGroup::add_change_variable( std::string in_name ) {
6✔
427

428
    REF2 * ref2 ;
429

430
    ref2 = ref_attributes(in_name.c_str()) ;
6✔
431

432
    if ( ref2 == NULL || ref2->attr == NULL ) {
6✔
NEW
433
        message_publish(MSG_WARNING, "DR Group \"%s\" : Could not find Data Record change variable %s.\n", group_name.c_str(), in_name.c_str()) ;
×
434
        return(-1) ;
×
435
    }
436

437
    Trick::DataRecordBuffer * new_var = new Trick::DataRecordBuffer ;
6✔
438
    new_var->ref = ref2 ;
6✔
439
    new_var->name = in_name;
6✔
440
    new_var->buffer = (char *)malloc(ref2->attr->size) ;
6✔
441
    new_var->last_value =  NULL ;
6✔
442
    memcpy(new_var->buffer , ref2->address , ref2->attr->size) ;
6✔
443
    change_buffer.push_back(new_var) ;
6✔
444

445
    return(0) ;
6✔
446

447
}
448

449
bool Trick::DataRecordGroup::isSupportedType(REF2 * ref2, std::string& message) {
548✔
450
    if (ref2->attr->type == TRICK_STRING || ref2->attr->type == TRICK_STL || ref2->attr->type == TRICK_STRUCTURED) {
548✔
451
        message = "Cannot Data Record variable " + std::string(ref2->reference) + " of unsupported type " + std::to_string(ref2->attr->type);
2✔
452
        return false;
2✔
453
    }
454
    
455
    // If this is an array and not a single value, don't record it
456
    if (ref2->num_index != ref2->attr->num_index) {
546✔
457
        message = "Cannot Data Record arrayed variable " + std::string(ref2->reference);
4✔
458
        return false;
4✔
459
    }
460

461
    return true;
542✔
462
}
463

464
/**
465
@details
466
-# The simulation output directory is retrieved from the CommandLineArguments
467
-# The log header file is created
468
   -# The endianness of the log file is written to the log header.
469
   -# The names of the parameters contained in the log file are written to the header.
470
-# Memory buffers are allocated to store simulation data
471
-# The DataRecordGroupObject (a derived SimObject) is added to the Scheduler.
472
*/
473
int Trick::DataRecordGroup::init(bool is_restart) {
58✔
474

475
    unsigned int jj ;
476
    int ret ;
477

478
    // reset counter here so we can "re-init" our recording
479
    buffer_num = writer_num = total_bytes_written = 0 ;
58✔
480

481
    output_dir = command_line_args_get_output_dir() ;
58✔
482
    /* this is the common part of the record file name, the format specific will add the correct suffix */
483
    file_name = output_dir + "/log_" + group_name ;
58✔
484

485
    pthread_mutex_init(&buffer_mutex, NULL);
58✔
486

487
    // Allocate recording space for time.
488
    rec_buffer[0]->buffer = (char *)calloc(max_num , rec_buffer[0]->ref->attr->size) ;
58✔
489
    rec_buffer[0]->last_value = (char *)calloc(1 , rec_buffer[0]->ref->attr->size) ;
58✔
490

491
    /* Loop through all variables looking up names.  Allocate recording space
492
       according to size of the variable */
493
    for (jj = 1; jj < rec_buffer.size() ; jj++) {
644✔
494
        Trick::DataRecordBuffer * drb = rec_buffer[jj] ;
586✔
495
        if ( drb->ref_searched == false ) {
586✔
496
            REF2 * ref2 ;
497

498
            ref2 = ref_attributes(drb->name.c_str()) ;
548✔
499
            if ( ref2 == NULL || ref2->attr == NULL ) {
548✔
NEW
500
                message_publish(MSG_WARNING, "DR Group \"%s\" : Could not find Data Record variable %s.\n", group_name.c_str(), drb->name.c_str()) ;
×
501
                rec_buffer.erase(rec_buffer.begin() + jj--) ;
×
502
                delete drb ;
×
503
                continue ;
×
504
            } else {
505
                std::string message;
548✔
506
                if (!isSupportedType(ref2, message)) {
548✔
507
                    message_publish(MSG_WARNING, "DR Group \"%s\" : %s\n", group_name.c_str(), message.c_str()) ;
6✔
508
                    rec_buffer.erase(rec_buffer.begin() + jj--) ;
6✔
509
                    delete drb ;
6✔
510
                    continue ;
6✔
511
                } else {
512
                    drb->ref = ref2 ;
542✔
513
                }
514
            }
515
        }
516
        if ( drb->alias.compare("") ) {
580✔
517
            drb->ref->reference = strdup(drb->alias.c_str()) ;
16✔
518
        }
519
        drb->last_value = (char *)calloc(1 , drb->ref->attr->size) ;
580✔
520
        drb->buffer = (char *)calloc(max_num , drb->ref->attr->size) ;
580✔
521
        drb->ref_searched = true ;
580✔
522
    }
523

524
    write_header() ;
58✔
525

526
    // call format specific initialization to open destination and write header
527
    ret = format_specific_init() ;
58✔
528

529
    if(!is_restart)
58✔
530
    {
531
        if(!check_if_rates_are_valid())
49✔
532
        {
NEW
533
            disable();
×
NEW
534
            return (1);
×
535
        }
536
        long long curr_tics = exec_get_time_tics();
49✔
537
        write_job->next_tics = curr_tics;
49✔
538

539
        long long next_next_tics = calculate_next_logging_tic(write_job->next_tics);
49✔
540
        write_job->cycle_tics = next_next_tics - curr_tics;
49✔
541
        write_job->cycle = (double)write_job->cycle_tics / Trick::JobData::time_tic_value;
49✔
542
    }
543

544
    // set the inited flag to true when all initialization is done
545
    if ( ret == 0 ) {
58✔
546
        inited = true ;
58✔
547
    }
548

549
    return(0) ;
58✔
550

551
}
552

553
void Trick::DataRecordGroup::configure_jobs(DR_Type type) {
616✔
554
    switch(type) {
616✔
555
    default:
53✔
556
        // run the restart job in phase 60001
557
        add_job(0, 5, (char *)"restart", NULL, 1.0, (char *)"restart", (char *)"TRK", 60001) ;
53✔
558

559
    case DR_Type::DR_Type_FrameLogDataRecord:
616✔
560
        // add_jobs_to_queue will fill in job_id later
561
        // make the init job run after all other initialization jobs but before the post init checkpoint
562
        // job so users can allocate memory in initialization jobs and checkpointing data rec groups will work
563
        add_job(0, 1, (char *)"initialization", NULL, default_cyle, (char *)"init", (char *)"TRK", 65534) ;
616✔
564
        add_job(0, 2, (char *)"end_of_frame", NULL, 1.0, (char *)"write_data", (char *)"TRK") ;
616✔
565
        add_job(0, 3, (char *)"checkpoint", NULL, 1.0, (char *)"checkpoint", (char *)"TRK") ;
616✔
566
        add_job(0, 4, (char *)"post_checkpoint", NULL, 1.0, (char *)"clear_checkpoint_vars", (char *)"TRK") ;
616✔
567
        add_job(0, 6, (char *)"shutdown", NULL, 1.0, (char *)"shutdown", (char *)"TRK") ;
616✔
568

569
        write_job = new Trick::DataRecordGroupJobData(*this, 0, 99, (char *)job_class.c_str(), NULL, default_cyle, (char *)"data_record" , (char *)"TRK") ;
616✔
570
        jobs.push_back(write_job) ;
616✔
571
        break ;
616✔
572
    }
573
    write_job->set_system_job_class(true);
616✔
574
}
616✔
575

576
int Trick::DataRecordGroup::checkpoint() {
13✔
577
    unsigned int jj ;
578

579
    /*
580
       Save the names of the variables and the aliases to the checkpoint,
581
       the rest of the DataRecordBuffer will be reconstructed during restart.
582
       The first variable is time which we do not have to save.
583
     */
584
    if ( rec_buffer.size() > 1 ) {
13✔
585
        num_variable_names = rec_buffer.size() - 1 ;
13✔
586
        variable_names = (char **)TMM_declare_var_1d("char *", (int)rec_buffer.size() - 1) ;
13✔
587
        variable_alias = (char **)TMM_declare_var_1d("char *", (int)rec_buffer.size() - 1) ;
13✔
588

589
        for (jj = 1; jj < rec_buffer.size() ; jj++) {
60✔
590
            Trick::DataRecordBuffer * drb = rec_buffer[jj] ;
47✔
591

592
            variable_names[jj-1] = TMM_strdup((char *)drb->name.c_str()) ;
47✔
593
            variable_alias[jj-1] = TMM_strdup((char *)drb->alias.c_str()) ;
47✔
594
        }
595
    }
596

597
    /*
598
       Save the names of the change variables and the aliases to the checkpoint,
599
       the rest of the DataRecordBuffer will be reconstructed during restart
600
     */
601
    if ( change_buffer.size() > 0 ) {
13✔
602
        num_change_variable_names = change_buffer.size() ;
2✔
603
        change_variable_names = (char **)TMM_declare_var_1d("char *", (int)change_buffer.size()) ;
2✔
604
        change_variable_alias = (char **)TMM_declare_var_1d("char *", (int)change_buffer.size()) ;
2✔
605

606
        for (jj = 0; jj < change_buffer.size() ; jj++) {
4✔
607
            Trick::DataRecordBuffer * drb = change_buffer[jj] ;
2✔
608

609
            change_variable_names[jj] = TMM_strdup((char *)drb->name.c_str()) ;
2✔
610
            change_variable_alias[jj] = TMM_strdup((char *)drb->alias.c_str()) ;
2✔
611
        }
612
    }
613

614
    return 0 ;
13✔
615
}
616

617
void Trick::DataRecordGroup::clear_checkpoint_vars() {
55✔
618
    
619
    if ( variable_names ) {
55✔
620
        for(unsigned int jj = 0; jj < num_variable_names; jj++) {
84✔
621
            TMM_delete_var_a(variable_names[jj]);
62✔
622
        }
623
        TMM_delete_var_a(variable_names) ;
22✔
624
    }
625

626
    if ( variable_alias ) {
55✔
627
        for(unsigned int jj = 0; jj < num_variable_names; jj++) {
84✔
628
            TMM_delete_var_a(variable_alias[jj]);
62✔
629
        }
630
        TMM_delete_var_a(variable_alias) ;
22✔
631
    }
632

633
    if ( change_variable_names ) {
55✔
634
        for(unsigned int jj = 0; jj < num_change_variable_names; jj++) {
8✔
635
            TMM_delete_var_a(change_variable_names[jj]);
4✔
636
        }
637
        TMM_delete_var_a(change_variable_names) ;
4✔
638
    }
639

640
    if ( change_variable_alias ) {
55✔
641
        for(unsigned int jj = 0; jj < num_change_variable_names; jj++) {
8✔
642
            TMM_delete_var_a(change_variable_alias[jj]);
4✔
643
        }
644
        TMM_delete_var_a(change_variable_alias) ;
4✔
645
    }
646

647
    variable_names = NULL ;
55✔
648
    variable_alias = NULL ;
55✔
649
    change_variable_names = NULL ;
55✔
650
    change_variable_alias = NULL ;
55✔
651
    num_variable_names = 0 ;
55✔
652
    num_change_variable_names = 0 ;
55✔
653
}
55✔
654

655
int Trick::DataRecordGroup::restart() {
9✔
656
    std::vector <Trick::DataRecordBuffer *>::iterator drb_it ;
9✔
657

658
    /* delete the current rec_buffer */
659
    for ( drb_it = rec_buffer.begin() ; drb_it != rec_buffer.end() ; ++drb_it ) {
18✔
660
        delete *drb_it ;
9✔
661
    }
662
    rec_buffer.clear() ;
9✔
663
    /* Add back the time variable */
664
    add_time_variable() ;
9✔
665

666
    /* delete the current change_buffer contents */
667
    for ( drb_it = change_buffer.begin() ; drb_it != change_buffer.end() ; ++drb_it ) {
9✔
668
        delete *drb_it ;
×
669
    }
670
    change_buffer.clear() ;
9✔
671

672
    unsigned int jj ;
673
    /* add the variable names listed in the checkpoint file */
674
    for ( jj = 0 ; jj < num_variable_names ; jj++ ) {
24✔
675
        add_variable( variable_names[jj] , variable_alias[jj] ) ;
15✔
676
    }
677
    for ( jj = 0 ; jj < num_change_variable_names ; jj++ ) {
11✔
678
        add_change_variable( change_variable_names[jj] ) ;
2✔
679
    }
680

681
    clear_checkpoint_vars() ;
9✔
682

683
    // set the write job class to what is in the checkpoint file.
684
    write_job->job_class_name = job_class ;
9✔
685

686
    // reset the sim_object name.
687
    name = std::string("data_record_group_") + group_name ;
9✔
688

689
    /* call init to open the recording file and look up variable name addresses */
690
    init(true) ;
9✔
691

692
    return 0 ;
9✔
693
}
694

695
int Trick::DataRecordGroup::write_header() {
58✔
696

697
    unsigned int jj ;
698
    std::string header_name ;
116✔
699
    std::fstream out_stream ;
116✔
700

701
    /*! create the header file used by the GUIs */
702
    header_name = output_dir + "/log_" + group_name + ".header" ;
58✔
703

704
    out_stream.open(header_name.c_str(), std::fstream::out ) ;
58✔
705
    if ( ! out_stream  ||  ! out_stream.good() ) {
58✔
706
        return -1;
×
707
    }
708

709
    /* Header file first line is created in format specific header */
710
    out_stream << "log_" << group_name ;
58✔
711

712
    format_specific_header(out_stream) ;
58✔
713

714
    /* Output the file name, variable size, units, and variable name
715
     * to the rest of recorded data header file.
716
     * (e.g. file_name  C_type  units  sim_name)
717
     * Note: "sys.exec.out.time" should be the first variable in the buffer.
718
     */
719
    for (jj = 0; jj < rec_buffer.size() ; jj++) {
696✔
720
        /*! recording single data item */
721
        out_stream << "log_" << group_name << "\t"
638✔
722
            << type_string(rec_buffer[jj]->ref->attr->type,
1,276✔
723
                           rec_buffer[jj]->ref->attr->size) << "\t"
638✔
724
            << std::setw(6) ;
1,276✔
725

726
        if ( rec_buffer[jj]->ref->attr->mods & TRICK_MODS_UNITSDASHDASH ) {
638✔
727
            out_stream << "--" ;
4✔
728
        } else {
729
            out_stream << rec_buffer[jj]->ref->attr->units ;
634✔
730
        }
731
        out_stream << "\t" << rec_buffer[jj]->ref->reference << std::endl ;
638✔
732
    }
733

734
    // Send all unwritten characters in the buffer to its output/file.
735
    out_stream.flush() ;
58✔
736
    out_stream.close() ;
58✔
737

738
    return(0) ;
58✔
739

740
}
741

742
int Trick::DataRecordGroup::data_record(double in_time) {
4,754,662✔
743

744
    unsigned int jj ;
745
    unsigned int buffer_offset ;
746
    Trick::DataRecordBuffer * drb ;
747
    bool change_detected = false ;
4,754,662✔
748

749
    curr_time_dr_job = in_time;
4,754,662✔
750

751
    //TODO: does not handle bitfields correctly!
752
    if ( record == true ) {
4,754,662✔
753
        if ( freq != DR_Always ) {
4,754,662✔
754
            for (jj = 0; jj < change_buffer.size() ; jj++) {
1,644✔
755
                drb = change_buffer[jj] ;
822✔
756
                REF2 * ref = drb->ref ;
822✔
757
                if ( ref->pointer_present == 1 ) {
822✔
758
                    ref->address = follow_address_path(ref) ;
×
759
                }
760
                if ( memcmp( drb->buffer , drb->ref->address , drb->ref->attr->size) ) {
822✔
761
                    change_detected = true ;
28✔
762
                    memcpy( drb->buffer , drb->ref->address , drb->ref->attr->size) ;
28✔
763
                }
764
            }
765

766
        }
767

768
        // Record data if frequency is always, or at the first call (buffer_num == 0), or a change was detected
769
        // Added condition to always record at the first call which might be also a checkpoint restart to ensure
770
        // data is recorded at least once for change based recording (DR_Changes and DR_Changes_Step).
771
        if ( freq == DR_Always || buffer_num == 0 || change_detected == true ) {
4,754,662✔
772

773
            // If this is not the ring buffer and
774
            // we are going to have trouble fitting 2 data sets then write the data now.
775
            if ( buffer_type != DR_Ring_Buffer ) {
4,753,874✔
776
                if ( buffer_num - writer_num >= (max_num - 2) ) {
4,494,674✔
777
                    write_data(true) ;
1✔
778
                }
779
            }
780

781
            curr_time = in_time ;
4,753,874✔
782

783
            if ( freq == DR_Changes_Step ) {
4,753,874✔
784
                buffer_offset = buffer_num % max_num ;
×
785
                *((double *)(rec_buffer[0]->last_value)) = in_time ;
×
786
                for (jj = 0; jj < rec_buffer.size() ; jj++) {
×
787
                    drb = rec_buffer[jj] ;
×
788
                    REF2 * ref = drb->ref ;
×
UNCOV
789
                    int param_size = ref->attr->size ;
×
790
                    if ( buffer_offset == 0 ) {
×
UNCOV
791
                       drb->curr_buffer = drb->buffer ;
×
792
                    } else {
793
                       drb->curr_buffer += param_size ;
×
794
                    }
795
                    switch ( param_size ) {
×
796
                        case 8:
×
797
                            *(int64_t *)drb->curr_buffer = *(int64_t *)drb->last_value ;
×
798
                            break ;
×
799
                        case 4:
×
800
                            *(int32_t *)drb->curr_buffer = *(int32_t *)drb->last_value ;
×
801
                            break ;
×
802
                        case 2:
×
803
                            *(int16_t *)drb->curr_buffer = *(int16_t *)drb->last_value ;
×
804
                            break ;
×
805
                        case 1:
×
806
                            *(int8_t *)drb->curr_buffer = *(int8_t *)drb->last_value ;
×
807
                            break ;
×
UNCOV
808
                        default:
×
UNCOV
809
                            memcpy( drb->curr_buffer , drb->last_value , param_size ) ;
×
810
                            break ;
×
811
                    }
812
                }
UNCOV
813
                buffer_num++ ;
×
814
            }
815

816
            buffer_offset = buffer_num % max_num ;
4,753,874✔
817
            for (jj = 0; jj < rec_buffer.size() ; jj++) {
39,654,951✔
818
                drb = rec_buffer[jj] ;
34,901,077✔
819
                REF2 * ref = drb->ref ;
34,901,077✔
820
                if ( ref->pointer_present == 1 ) {
34,901,077✔
UNCOV
821
                    ref->address = follow_address_path(ref) ;
×
822
                }
823
                int param_size = ref->attr->size ;
34,901,077✔
824
                if ( buffer_offset == 0 ) {
34,901,077✔
825
                   drb->curr_buffer = drb->buffer ;
916✔
826
                } else {
827
                   drb->curr_buffer += param_size ;
34,900,161✔
828
                }
829
                /**
830
                 * While the typical idiom is something like:
831
                 * 1. previous_value = current_value
832
                 * 2. current_value = new_value
833
                 * That is incorrect here, as curr_buffer is a pointer that has already been
834
                 * incremented to the next value's location. We therefore set *curr_buffer and
835
                 * *last_value to the same value, which results in the DR_Changes_Step loop above
836
                 * correctly using this value as the first point of the step change on the next
837
                 * call to this function.
838
                 */
839
                switch ( param_size ) {
34,901,077✔
840
                    case 8:
33,945,579✔
841
                        *(int64_t *)drb->last_value = *(int64_t *)drb->curr_buffer = *(int64_t *)ref->address ;
33,945,579✔
842
                        break ;
33,945,579✔
843
                    case 4:
954,778✔
844
                        *(int32_t *)drb->last_value = *(int32_t *)drb->curr_buffer = *(int32_t *)ref->address ;
954,778✔
845
                        break ;
954,778✔
846
                    case 2:
650✔
847
                        *(int16_t *)drb->last_value = *(int16_t *)drb->curr_buffer = *(int16_t *)ref->address ;
650✔
848
                        break ;
650✔
849
                    case 1:
70✔
850
                        *(int8_t *)drb->last_value = *(int8_t *)drb->curr_buffer = *(int8_t *)ref->address ;
70✔
851
                        break ;
70✔
852
                    default:
×
UNCOV
853
                        memcpy( drb->curr_buffer , ref->address , param_size ) ;
×
UNCOV
854
                        memcpy( drb->last_value , drb->curr_buffer , param_size ) ;
×
UNCOV
855
                        break ;
×
856
                }
857
            }
858
            buffer_num++ ;
4,753,874✔
859
        }
860
    }
861

862
    long long curr_tics = (long long)round(in_time * Trick::JobData::time_tic_value);
4,754,662✔
863
    advance_log_tics_given_curr_tic(curr_tics);
4,754,662✔
864

865
    write_job->next_tics = calculate_next_logging_tic(curr_tics);
4,754,662✔
866
    write_job->cycle_tics = write_job->next_tics - curr_tics;
4,754,662✔
867
    write_job->cycle = (double)write_job->cycle_tics / Trick::JobData::time_tic_value;
4,754,662✔
868

869
    return(0) ;
4,754,662✔
870
}
871

872
bool Trick::DataRecordGroup::check_if_rates_are_valid()
49✔
873
{
874
    // long long curr_tics = exec_get_time_tics();
875
    long long tic_value = Trick::JobData::time_tic_value;
49✔
876
    bool areValid = true;
49✔
877

878
    for(size_t ii = 0; ii < logging_rates.size(); ++ii)
104✔
879
    {
880
        double logging_rate = logging_rates[ii].rate_in_seconds;
55✔
881
        int ret = check_if_rate_is_valid(logging_rate);
55✔
882
        if(ret != 0){
55✔
NEW
883
            emit_rate_error(ret, ii, logging_rate);
×
NEW
884
            areValid = false;
×
885
        }
886
    }
887
    return areValid;
49✔
888
}
889

890
void Trick::DataRecordGroup::emit_rate_error(int rate_err_code, size_t log_idx, double err_rate)
3✔
891
{
892
    long long tic_value = Trick::JobData::time_tic_value;
3✔
893
    if(rate_err_code == 1) {
3✔
894
        message_publish(
1✔
895
            MSG_ERROR,
896
            "DataRecordGroup ERROR: DR Group \"%s\" : Cycle for %lu logging rate idx is less than time tic value. cycle = "
897
            "%16.12f, time_tic = %16.12f\n",
898
            group_name.c_str(),
899
            log_idx,
900
            err_rate,
901
            tic_value);
902
    } else if(rate_err_code == 2)
2✔
903
    {
904
        long long cycle_tics = (long long)round(err_rate * tic_value);
2✔
905
        message_publish(MSG_ERROR,
2✔
906
                        "DataRecordGroup ERROR: DR Group \"%s\" : Cycle for %lu logging rate idx cannot be exactly scheduled "
907
                        "with time tic value. "
908
                        "cycle = %16.12f, cycle_tics = %lld , time_tic = %16.12f\n",
909
                        group_name.c_str(),
910
                        log_idx,
911
                        err_rate,
912
                        cycle_tics,
913
                        1.0 / tic_value);
914
    }
915
}
3✔
916

917
 int Trick::DataRecordGroup::check_if_rate_is_valid(double test_rate)
69✔
918
 {
919
    long long tic_value = Trick::JobData::time_tic_value;
69✔
920
    int ret = 0;
69✔
921

922
    double logging_rate = test_rate;
69✔
923
    if(logging_rate < (1.0 / tic_value))
69✔
924
    {
925
        ret = 1;        
1✔
926
    } else {        
927
        /* Calculate the if the cycle_tics would be a whole number  */
928
        double test_rem = fmod(logging_rate * (double)tic_value, 1.0);
68✔
929

930
        if(test_rem > 0.001)
68✔
931
        {
932
            ret = 2;
2✔
933
        }
934
    }
935
    
936
    return ret;
69✔
937
 }
938

939

940
/**
941
 * Loop through the required logging rates and calculate the
942
 * next logging time in tics.
943
 * @return Next logging time in tics,
944
 */
945
long long Trick::DataRecordGroup::calculate_next_logging_tic(long long min_tic)
4,754,733✔
946
{
947
    long long ticOfCycleToProcess = std::numeric_limits<long long>::max();
4,754,733✔
948

949
    // Loop over all the logging rates. If the logging rate's next tic is equal to the min_tic, test against
950
    // that rate's next cycle from min. Find the smallest next tic 
951
    for(size_t cycleIndex = 0; cycleIndex < logging_rates.size(); ++cycleIndex)
26,790,035✔
952
    {
953
        long long logNextTic = logging_rates[cycleIndex].next_cycle_in_tics;
22,035,302✔
954

955
        if(logNextTic == min_tic)
22,035,302✔
956
        {
957
            logNextTic += logging_rates[cycleIndex].rate_in_tics;
68✔
958
        }
959

960
        if(logNextTic < ticOfCycleToProcess)
22,035,302✔
961
        {
962
            ticOfCycleToProcess = logNextTic;
15,511,631✔
963
        }
964
    }
965

966
    return ticOfCycleToProcess;
4,754,733✔
967
}
968

969
/**
970
 * Loop through the required logging rates and advance the next cycle tics of matching rates
971
 * @param curr_tic_in - time in tics to match and advance the next cycle tic
972
 */
973
void Trick::DataRecordGroup::advance_log_tics_given_curr_tic(long long curr_tic_in)
4,754,673✔
974
{
975
    for(size_t cycleIndex = 0; cycleIndex < logging_rates.size(); ++cycleIndex)
26,789,898✔
976
    {
977
        long long & logNextTic = logging_rates[cycleIndex].next_cycle_in_tics;
22,035,225✔
978
        logNextTic = LoggingCycle::calc_next_tics_on_or_after_input_tic(curr_tic_in, logging_rates[cycleIndex].rate_in_tics);
22,035,225✔
979
        if(logNextTic <= curr_tic_in)
22,035,225✔
980
        {
981
            logNextTic += logging_rates[cycleIndex].rate_in_tics;
6,920,098✔
982
        }
983
    }
984
}
4,754,673✔
985

986
int Trick::DataRecordGroup::write_data(bool must_write) {
415,587✔
987

988
    unsigned int local_buffer_num ;
989
    unsigned int num_to_write ;
990
    unsigned int writer_offset ;
991

992
    if ( record and inited and (buffer_type == DR_No_Buffer or must_write) and (total_bytes_written <= max_file_size)) {
415,587✔
993

994
        // buffer_mutex is used in this one place to prevent forced calls of write_data
995
        // to not overwrite data being written by the asynchronous thread.
996
        pthread_mutex_lock(&buffer_mutex) ;
43,926✔
997

998
        local_buffer_num = buffer_num ;
43,926✔
999
        if ( (local_buffer_num - writer_num) > max_num ) {
43,926✔
UNCOV
1000
            num_to_write = max_num ;
×
1001
        } else {
1002
            num_to_write = (local_buffer_num - writer_num) ;
43,926✔
1003
        }
1004
        writer_num = local_buffer_num - num_to_write ;
43,926✔
1005

1006
        //! This loop pulls a "row" of time homogeneous data and writes it to the file
1007
        while ( writer_num != local_buffer_num ) {
4,797,800✔
1008

1009
            writer_offset = writer_num % max_num ;
4,753,874✔
1010
            //! keep record of bytes written to file. Default max is 1GB
1011
            total_bytes_written += format_specific_write_data(writer_offset) ;
4,753,874✔
1012
            writer_num++ ;
4,753,874✔
1013

1014
        }
1015

1016
        if(!max_size_warning && (total_bytes_written > max_file_size)) {
43,926✔
1017
            std::cerr << "WARNING: Data record max file size " << (static_cast<double>(max_file_size))/(1<<20) << "MB reached.\n"
×
UNCOV
1018
            "https://nasa.github.io/trick/documentation/simulation_capabilities/Data-Record#changing-the-max-file-size-of-a-data-record-group-ascii-and-binary-only" 
×
UNCOV
1019
            << std::endl;
×
UNCOV
1020
            max_size_warning = true;
×
1021
        }
1022

1023
        pthread_mutex_unlock(&buffer_mutex) ;
43,926✔
1024

1025
    }
1026

1027
    return 0 ;
415,573✔
1028
}
1029

1030
int Trick::DataRecordGroup::enable() {
38✔
1031
    record = true ;
38✔
1032
    return(0) ;
38✔
1033
}
1034

UNCOV
1035
int Trick::DataRecordGroup::disable() {
×
UNCOV
1036
    record = false ;
×
UNCOV
1037
    return(0) ;
×
1038
}
1039

1040
int Trick::DataRecordGroup::shutdown() {
55✔
1041

1042
    // Force write out all data
1043
    record = true ; // If user disabled group, make sure any recorded data gets written out
55✔
1044
    write_data(true) ;
55✔
1045
    format_specific_shutdown() ;
55✔
1046

1047
    remove_all_variables();
55✔
1048

1049
    // remove_all_variables does not remove sim time
1050
    if(!rec_buffer.empty()){
55✔
1051
        delete rec_buffer[0];
55✔
1052
        rec_buffer.clear();
55✔
1053
    }
1054

1055
    if ( writer_buff ) {
55✔
1056
        free(writer_buff) ;
55✔
1057
        writer_buff = NULL ;
55✔
1058
    }
1059

1060
    return 0 ;
55✔
1061
}
1062

1063
std::string Trick::DataRecordGroup::type_string( int item_type, int item_size ) {
638✔
1064
    switch (item_type) {
638✔
1065
        case TRICK_CHARACTER:
2✔
1066
                return "char";
2✔
1067
                break;
1068
        case TRICK_UNSIGNED_CHARACTER:
2✔
1069
                return "unsigned_char";
2✔
1070
                break;
UNCOV
1071
        case TRICK_STRING:
×
UNCOV
1072
                return "string";
×
1073
                break;
1074
        case TRICK_SHORT:
5✔
1075
                return "short";
5✔
1076
                break;
1077
        case TRICK_UNSIGNED_SHORT:
5✔
1078
                return "unsigned_short";
5✔
1079
                break;
1080
        case TRICK_ENUMERATED:
77✔
1081
        case TRICK_INTEGER:
1082
                return "int";
77✔
1083
                break;
1084
        case TRICK_UNSIGNED_INTEGER:
5✔
1085
                return "unsigned_int";
5✔
1086
                break;
1087
        case TRICK_LONG:
5✔
1088
                return "long";
5✔
1089
                break;
1090
        case TRICK_UNSIGNED_LONG:
5✔
1091
                return "unsigned_long";
5✔
1092
                break;
1093
        case TRICK_FLOAT:
5✔
1094
                return "float";
5✔
1095
                break;
1096
        case TRICK_DOUBLE:
460✔
1097
                if ( single_prec_only ) {
460✔
UNCOV
1098
                        return "float";
×
1099
                }
1100
                else {
1101
                        return "double";
460✔
1102
                }
1103
                break;
1104
        case TRICK_BITFIELD:
28✔
1105
                if (item_size == sizeof(int)) {
28✔
1106
                        return "int";
28✔
1107
                } else if (item_size == sizeof(short)) {
×
UNCOV
1108
                        return "short";
×
1109
                } else {
UNCOV
1110
                        return "char";
×
1111
                }
1112
                break;
1113
        case TRICK_UNSIGNED_BITFIELD:
28✔
1114
                if (item_size == sizeof(int)) {
28✔
1115
                        return "unsigned_int";
28✔
1116
                } else if (item_size == sizeof(short)) {
×
UNCOV
1117
                        return "unsigned_short";
×
1118
                } else {
UNCOV
1119
                        return "unsigned_char";
×
1120
                }
1121
                break;
1122
        case TRICK_LONG_LONG:
5✔
1123
                return "long_long";
5✔
1124
                break;
1125
        case TRICK_UNSIGNED_LONG_LONG:
2✔
1126
                return "unsigned_long_long";
2✔
1127
                break;
1128
        case TRICK_BOOLEAN:
4✔
1129
#if ( __sun | __APPLE__ )
1130
                return "int";
1131
#else
1132
                return "unsigned_char";
4✔
1133
#endif
1134
                break;
1135
    }
UNCOV
1136
    return "";
×
1137
}
1138

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