-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathsight_structure.C
3324 lines (2770 loc) · 138 KB
/
sight_structure.C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2013, Lawrence Livermore National Security, LLC.
// Produced at the Lawrence Livermore National Laboratory.
// Written by the Greg Bronevetsky <bronevetsky1@llnl.gov> / <greg@bronevetsky.com>.
//
// LLNL-CODE-642002
// All rights reserved.
//
// This file is part of Sight. For details, see https://e-reports-ext.llnl.gov/pdf/781752.pdf or
// https://github.com/bronevet/sight.
//
// Licensed under the GNU Lesser General Public License (Lesser GPU) Version 2.1,
// February 1999; you may not use this file except in compliance with the License.
// The full licence is included in file LICENCE and you may obtain a copy of the
// License at:
// https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
// implied. See the License for the specific language governing
// permissions and limitations under the license.
////////////////////////////////////////////////////////////////////////////////
// Licence information included in file LICENCE
#include "sight_common_internal.h"
#include "sight_structure_internal.h"
#include <fstream>
#include <iostream>
#include <sstream>
#include <unistd.h>
#include <stdlib.h>
#include <assert.h>
#include <iostream>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <limits.h>
#include "binreloc.h"
#include <errno.h>
#include "getAllHostnames.h"
#include "utils.h"
#include "fdstream.h"
#include "process.h"
#include "process.C"
using namespace std;
using namespace sight::common;
// Support for text diffs
#include <dtl/dtl.hpp>
using namespace std;
using namespace dtl;
using dtl::Diff;
namespace sight {
namespace structure{
// Merges the two strings into one
string mergeText(const string& a, const string& b) {
typedef char elem;
typedef string sequence;
Diff< elem, sequence > d(a, b);
d.compose();
vector< pair< char, elemInfo > > ses_v = d.getSes().getSequence();
string merged;
for(int i=0; i<ses_v.size(); i++)
merged += ses_v[i].first;
return merged;
}
/***************
***** dbg *****
***************/
// Declaration of dbgStream to which all structure output will be written
//ThreadLocalDbgStream dbg;
ThreadLocalStorageDbgStream dbg;
//__thread dbgStream dbg;
// Keeps track whether we've initialized Sight for the main thread
bool initializedDebugMainThread=false;
// Keeps track of whether we've initialized Sight in each individual thread
ThreadLocalStorage1<bool, bool> initializedDebugThisThread(false);
// Cached copy of Sight properties set for the main thread, which may be reused
// to initialize the Sight properties in other subsequently-created threads.
std::map<std::string, std::string> mainThreadSightProps;
/*******************
***** soStack *****
*******************/
// The stack of sightObjs that are currently in scope. We declare it as a static inside this function
// to make it possible to ensure that the global soStack is constructed before it is used and therefore
// destructed after all of its user objects are destructed.
// There is a separate stack for each outgoing stream.
ThreadLocalStorageMap<dbgStream*, std::list<sightObj*> > staticSoStack;
// Maps each thread's ID to a pointer to their soStacks. This is useful for cleaning up the
// states of all the different threads when the process receives an abort signal.
std::map<pthread_t, map<dbgStream*, std::list<sightObj*> >* > threadSoStacks;
// Mutex that controls access to threadSoStacks.
pthread_mutex_t threadSoStacksMutex = PTHREAD_MUTEX_INITIALIZER;
std::list<sightObj*>& soStack(dbgStream* outStream) {
return staticSoStack[outStream];
}
std::map<dbgStream*, std::list<sightObj*> >& soStackAllStreams() {
return *(staticSoStack.get());
}
// Returns the depth of the sightObjects stack
int getSOStackDepth() {
return soStack(&dbg).size();
}
int getSOStackDepth(dbgStream* outStream) {
return soStack(outStream).size();
}
/*************************************
***** ThreadInitFinInstantiator *****
*************************************/
// The maximum unique ID assigned to any threadFuncs so far
int* ThreadInitFinInstantiator::maxUID;
// Keep track of the funcs that are currently registered to initialize and finalize threads
// Maps the string labels of funcs to their threadFuncs data structures
// Each threadFuncs has a unique ID that is used as the vertex number in the dependence
// graph, which is implemented using the Boost Graph Library
std::map<std::string, ThreadInitFinInstantiator::threadFuncs*>* ThreadInitFinInstantiator::funcsM;
// Vector of pointers to the threadFuncs in funcsM, where the index of each record is
// equal to its UID.
std::vector<ThreadInitFinInstantiator::threadFuncs*>* ThreadInitFinInstantiator::funcsV;
// Records the dependencies among the entries in funcs
boost::adjacency_list<boost::listS, boost::vecS, boost::directedS>* ThreadInitFinInstantiator::depGraph;
// Records the topological order among the funcs
std::deque<int>* ThreadInitFinInstantiator::topoOrder;
// Records whether the dependence graph is upto-date relative to the current
// entries in funcs
bool* ThreadInitFinInstantiator::depGraphUptoDate;
// The height of each thread's SOStack after it has been initialized. This is important
// for ensuring that on shutdown we destroy any sightObjects above this point before
// calling the finalization routines.
ThreadLocalStorage0<int> ThreadInitFinInstantiator::initializedSOStackHeight;
ThreadInitFinInstantiator::ThreadInitFinInstantiator() :
sight::common::LoadTimeRegistry("ThreadInitFinInstantiator",
ThreadInitFinInstantiator::init)
{ }
// Called exactly once for each class that derives from LoadTimeRegistry to initialize its static data structures.
void ThreadInitFinInstantiator::init() {
maxUID = new int;
*maxUID = 0;
funcsM = new std::map<std::string, threadFuncs*>();
funcsV = new std::vector<threadFuncs*>();
depGraph = new boost::adjacency_list<boost::listS, boost::vecS, boost::directedS>();
topoOrder = new std::deque<int>;
depGraphUptoDate = new bool;
*depGraphUptoDate = false;
}
// Adds the given initialization/finalization funcs under the given widet label.
// mustFollow - set of widget labels that the given functors must follow during initialization
// mustPrecede - set of widget labels that the given functors must precede during initialization
// finalization is performed in reverse
void ThreadInitFinInstantiator::addFuncs(const std::string& label, ThreadInitializer init, ThreadFinalizer fin,
const common::easyset<std::string>& mustFollow, const common::easyset<std::string>& mustPrecede) {
//cout << "ThreadInitFinInstantiator::addFuncs()"<<endl;
assert(funcsM);
assert(funcsV);
threadFuncs* funcs = new threadFuncs(init, fin, mustFollow, mustPrecede);
(*funcsM)[label] = funcs;
funcsV->push_back(funcs);
assert(funcsV->size() == funcs->UID+1);
// The dependency graph is no longer upto-date relative to funcs
*depGraphUptoDate = false;
depGraph->clear();
}
// Update the dependence graph based on funcs, if it not already upto-date
void ThreadInitFinInstantiator::computeDepGraph() {
//cout << pthread_self()<<": ThreadInitFinInstantiator::computeDepGraph() *depGraphUptoDate="<<*depGraphUptoDate<<endl;
if(*depGraphUptoDate) return;
// Iterate over funcs, adding each dependency to depGraph
int i=0;
//cout << pthread_self()<<": #funcsM="<<funcsM->size()<<endl;
// Map where the keys are the funcs that are not connected to any other funcs. These may be placed
// anywhere in the topological order. Initialized to contain all funcs, which are then removed
// as they're connected by edges
set<int> unconnected;
for(map<string, threadFuncs*>::iterator f=funcsM->begin(); f!=funcsM->end(); f++)
unconnected.insert(f->second->UID);
// Iterate over all funcs and connect them to their predecessors and successors via edges
for(map<string, threadFuncs*>::iterator f=funcsM->begin(); f!=funcsM->end(); f++) {
int numEdges=0;
// Add edges from all labels that the current func must follow to the current func
for(set<std::string>::iterator i=f->second->mustFollow.begin(); i!=f->second->mustFollow.end(); i++) {
// Find the current label in the funcs map and add an edge from it to f
std::map<std::string, threadFuncs*>::iterator other=funcsM->find(*i);
if(other != funcsM->end()) {
boost::add_edge(other->second->UID, f->second->UID, *depGraph);
unconnected.erase(other->second->UID);
unconnected.erase(f->second->UID);
}
}
// Add edges from the current func to all the labels that follow it
for(set<std::string>::iterator i=f->second->mustPrecede.begin(); i!=f->second->mustPrecede.end(); i++) {
// Find the current label in the funcs map and add an edge to it from f
std::map<std::string, threadFuncs*>::iterator other=funcsM->find(*i);
if(other != funcsM->end()) {
boost::add_edge(f->second->UID, other->second->UID, *depGraph);
unconnected.erase(other->second->UID);
unconnected.erase(f->second->UID);
}
}
}
// Topologically sort the funcs that are connected with dependence edges
boost::topological_sort(*depGraph, front_inserter(*topoOrder), vertex_index_map(boost::identity_property_map()));
// Append to topoOrder all the funcs that are not connected via edges
for(set<int>::iterator i=unconnected.begin(); i!=unconnected.end(); i++)
topoOrder->push_back(*i);
// The dependence graph is now upto date
*depGraphUptoDate = true;
}
// Calls all the initializers in their topological order
void ThreadInitFinInstantiator::initialize() {
computeDepGraph();
/*cout << pthread_self()<<": ThreadInitFinInstantiator::initialize()"<<endl;
for(std::map<std::string, threadFuncs*>::iterator i=funcsM->begin(); i!=funcsM->end(); i++)
cout << " "<<i->first<<": "<<i->second->UID<<endl;
cout << pthread_self()<<": #topoOrder="<<topoOrder->size()<<endl; */
for(deque<int>::iterator i=topoOrder->begin(); i!=topoOrder->end(); ++i) {
assert(funcsV->size()>*i);
(*funcsV)[*i]->init();
}
// Record the height of the thread's soStack after initialization has completed
initializedSOStackHeight = getSOStackDepth();
}
// Calls all the finalizers in their topological order (reverse of initializers)
void ThreadInitFinInstantiator::finalize() {
computeDepGraph();
// Trim this tread's stack down to its height immediately after initialization
sightObj::destroyThreadStack(initializedSOStackHeight);
// Call the finalizers
for(deque<int>::reverse_iterator i=topoOrder->rbegin(); i!=topoOrder->rend(); ++i) {
assert(funcsV->size()>*i);
(*funcsV)[*i]->fin();
}
}
std::string ThreadInitFinInstantiator::str() {
std::ostringstream s;
s << "[ThreadInitFinInstantiator:"<<endl;
s << " funcsM=(#"<<funcsM->size()<<"): "<<endl;
for(map<string, threadFuncs*>::iterator f=funcsM->begin(); f!=funcsM->end(); f++) {
s << f->first << " => "<<f->second<<endl;
}
s << "maxUID="<<*maxUID<<", depGraphUptoDate="<<*depGraphUptoDate<<"]";
return s.str();
}
// Records whether this is the main thread or not.
ThreadLocalStorage0<bool> isMainThread;
// Records a pointer to each thread's dbgStream. This is important for finalizing
// the main thread's data structures exactly when that thread's global dbg object
// is being destroyed.
ThreadLocalStorage0<dbgStream*> threadDbgStream;
// Returns whether the given dbgStream pointer refers to the global dbg object
// of the main thread.
bool isMainThreadDbg(dbgStream* ds)
{ return isMainThread && ds==threadDbgStream; }
// mainThread: Set to true if this function is being called from the main thread directly by the other.
// Set to false if it is called inside some other, subsequently-created thread from within Sight
void SightInit_internal(int argc, char** argv, string title, string workDir, bool mainThread);
// Records the information needed to call the application
ThreadLocalStorage1<bool, bool> saved_appExecInfo(false); // Indicates whether the application execution info has been saved
ThreadLocalStorage1<int, int> saved_argc(0);
ThreadLocalStorage1<char**, char**> saved_argv(NULL);
ThreadLocalStorage1<char*, char*> saved_execFile(NULL);
// The unique ID of this process' output stream
ThreadLocalStorage1<int, int> outputStreamID(0);
// Provides the output directory and run title as well as the arguments that are required to rerun the application
void SightInit(int argc, char** argv, string title, string workDir)
{
//cout << pthread_self() << ": SightInit() initializedDebugMainThread="<<initializedDebugMainThread<<", initializedDebugThisThread="<<initializedDebugThisThread<<endl;
// If Sight has already been initialized in the main thread, abort since main thread initialization can only be done directly by the user
if(initializedDebugMainThread) { cerr << "ERROR: Sight has been initialized multiple times!"<<endl; assert(0); }
// If Sight has been implicitly initialized in this thread, emit a warning since the parameters from explicit initialization are not being used
if(initializedDebugThisThread) { cerr << "WARNING: Sight has been initialized multiple times within this thread!"<<endl; }
SightInit_internal(argc, argv, title, workDir, true);
}
// Provides the output directory and run title as well but does not provide enough info to rerun the application
void SightInit(string title, string workDir)
{
// If Sight has already been initialized in the main thread, abort since main thread initialization can only be done directly by the user
if(initializedDebugMainThread) { cerr << "ERROR: Sight has been initialized multiple times!"<<endl; assert(0); }
// If Sight has been implicitly initialized in this thread, emit a warning since the parameters from explicit initialization are not being used
if(initializedDebugThisThread) { cerr << "WARNING: Sight has been initialized multiple times within this thread!"<<endl; }
SightInit_internal(0, NULL, title, workDir, true);
}
// Called from within Sight when some code region discovers that it is being invoked
// before Sight has been initialized on the current thread.
void SightInit_NewThread() {
//cout << pthread_self() << ": SightInit_NewThread() initializedDebugMainThread="<<initializedDebugMainThread<<", initializedDebugThisThread="<<initializedDebugThisThread<<endl;
SightInit_internal(0, NULL, "", "", false);
/*// If Sight has already been initialized in the main thread
if(initializedDebugMainThread) SightInit_Internal(0, NULL, "", "", false);
// Else, if Sight has not yet been initialized, we'll treat this thread as main
else SightInit_Internal(0, NULL, "dbg", "dbg", true);*/
}
// Low-level initialization of Sight that sets up some basics but does not allow users to use the dbg
// output stream to emit their debug output, meaning that other widgets cannot be used directly.
// Users that using this type of initialization must create their own dbgStreams and emit enter/exit
// tags to these streams directly without using any of the high-level APIs.
void SightInit_LowLevel()
{
loadSightConfig(configFileEnvVars("SIGHT_STRUCTURE_CONFIG", "SIGHT_CONFIG"));
initializedDebugMainThread = true;
initializedDebugThisThread = true;
}
// Comparison tag for the main thread that will live for the entire duration of the application.
// It must be aligned to the same comparison tag in the logs of other processes (set in
// SightInit_internal) and the non-main threads within this process (set in sight_pthread_create).
//comparison* mainThreadComp=NULL;
// mainThread: Set to true if this function is being called from the main thread directly by the other.
// Set to false if it is called inside some other, subsequently-created thread from within Sight
void SightInit_internal(int argc, char** argv, string title, string workDir, bool mainThread)
{
//cout << pthread_self()<<": SightInit_internal("+title+")"<<endl;
// Assign each pthread to a separate log based on its thread ID
/*if(mainThread) {
globalComparisonIDs.push_back(make_pair("pthread", std::string(txt()<<pthread_self())));
}*/
// Record whether this is the main thread
isMainThread = mainThread;
loadSightConfig(configFileEnvVars("SIGHT_STRUCTURE_CONFIG", "SIGHT_CONFIG"));
// Register AbortHandlerInstantiator::appExited to be called when the application calls exit.
// This must be done here since this method is called within main(), after all
// the global vars have been initialized. This means that when appExited() is
// called, none of them will have been destroyed yet.
atexit(AbortHandlerInstantiator::appExited);
// If this is the main thread, set the Sight properties to be used for serialization
if(mainThread) {
mainThreadSightProps["title"] = title;
mainThreadSightProps["workDir"] = workDir;
// Records whether we know the application's command line, which would enable us to call it
mainThreadSightProps["commandLineKnown"] = (argv!=NULL? "1": "0");
// If the command line is known, record it in mainThreadSightProps
if(argv!=NULL) {
mainThreadSightProps["argc"] = txt()<<argc;
for(int i=0; i<argc; i++)
mainThreadSightProps[txt()<<"argv_"<<i] = string(argv[i]);
BrInitError error;
int ret = br_init(&error);
if(!ret) {
cerr << "ERROR reading application's executable name! "<<
(error==BR_INIT_ERROR_NOMEM? "Cannot allocate memory." :
(error==BR_INIT_ERROR_OPEN_MAPS? "Cannot open /proc/self/maps "+string(strerror(errno)) :
(error==BR_INIT_ERROR_READ_MAPS? "The file format of /proc/self/maps is invalid; kernel bug?" :
(error==BR_INIT_ERROR_DISABLED? "BinReloc is disabled (the ENABLE_BINRELOC macro is not defined)" :
"???"
))))<<endl;
assert(0);
}
char* execFile = br_find_exe(NULL);
if(execFile==NULL) { cerr << "ERROR reading application's executable name after successful initialization!"<<endl; assert(0); }
mainThreadSightProps["execFile"] = execFile;
char cwd[FILENAME_MAX];
getcwd(cwd, FILENAME_MAX);
mainThreadSightProps["cwd"] = cwd;
// Record the names and values of all the environment variables
char** e = environ;
int numEnvVars=0;
while(*e) {
string var = *e;
int splitPoint = var.find("=");
//cout << *e << ": " << var.substr(0, splitPoint) << " => "<<var.substr(splitPoint+1)<<endl;
mainThreadSightProps[txt()<<"envName_"<<numEnvVars] = var.substr(0, splitPoint);
mainThreadSightProps[txt()<<"envVal_"<<numEnvVars] = var.substr(splitPoint+1);
numEnvVars++;
e++;
}
mainThreadSightProps["numEnvVars"] = txt()<<numEnvVars;
}
// Get all the aliases of the current host's name
//char hostname[10000]; // The name of the host that this application is currently executing on
//int ret = gethostname(hostname, 10000);
list<string> hostnames = getAllHostnames();
mainThreadSightProps["numHostnames"] = txt()<<hostnames.size();
{ int i=0;
for(list<string>::iterator h=hostnames.begin(); h!=hostnames.end(); h++, i++)
mainThreadSightProps[txt()<<"hostname_"<<i] = *h;
}
// Get the current user's username, using the environment if possible
char username[10000]; // The current user's user name
if(getenv("USER"))
strncpy(username, getenv("USER"), sizeof(username));
else if(getenv("USERNAME"))
strncpy(username, getenv("USERNAME"), sizeof(username));
else {
// If the username is not available from the environment, use the id command
ostringstream cmd;
cmd << "id --user --name";
FILE* fp = popen(cmd.str().c_str(), "r");
if(fp == NULL) { cerr << "Failed to run command \""<<cmd.str()<<"\"!"<<endl; assert(0); }
if(fgets(username, sizeof(username), fp) == NULL) { cerr << "Failed to read output of \""<<cmd.str()<<"\"!"<<endl; assert(0); }
pclose(fp);
}
mainThreadSightProps["username"] = string(username);
// Set the unique ID of this process' output stream
outputStreamID = getpid();
mainThreadSightProps["outputStreamID"] = txt()<<outputStreamID;
}
// Record the properties of this Sight object. We use the base mainThreadSightProps
// object but add this thread's unique ID to it.
map<string, string> thisThreadSightProps = mainThreadSightProps;
thisThreadSightProps["threadID"] = txt() << pthread_self();
// If this is not the main thread, append this thread's ID to the main thread's workDir
if(!mainThread) {
workDir = txt()<<mainThreadSightProps["workDir"]<<".thread_"<<pthread_self();
thisThreadSightProps["workDir"] = workDir;
}
properties* props = new properties();
props->add("sight", thisThreadSightProps);
//cout << pthread_self()<<": SightInit_internal("+title+") workDir="<<workDir<<endl;
// Create the directory structure for the structural information
// Main output directory
createDir(workDir, "");
// Directory where client-generated images will go
string imgDir = createDir(workDir, "html/dbg_imgs");
// Directory that widgets can use as temporary scratch space
string tmpDir = createDir(workDir, "html/tmp");
// Record that we've initialized Sight on the current thread
if(mainThread) initializedDebugMainThread = true;
initializedDebugThisThread = true;
dbg->init(props, title, workDir, imgDir, tmpDir);
// Record the address of this thread's dbgStream
threadDbgStream = dbg.get();
//cout << pthread_self() << ": SightThreadInit() mainThread="<<mainThread<<endl;
SightThreadInit();
}
void SightThreadInit() {
//cout << pthread_self()<<": SightThreadInit\n";
// Set this thread to be cancelable
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
// Register this thread's soStacks in threadSoStacks
pthread_mutex_lock(&threadSoStacksMutex);
threadSoStacks[pthread_self()] = staticSoStack.get();
ThreadInitFinInstantiator::initialize();
pthread_mutex_unlock(&threadSoStacksMutex);
}
void SightThreadFinalize() {
// cout << pthread_self()<<": SightThreadFinalize\n";
// Unregister this thread's soStacks from threadSoStacks
pthread_mutex_lock(&threadSoStacksMutex);
if(threadSoStacks.find(pthread_self()) != threadSoStacks.end()) {
// cout << pthread_self()<<": SightThreadFinalize()"<<endl;
threadSoStacks.erase(pthread_self());
ThreadInitFinInstantiator::finalize();
// Delete the comparison tags that wrap the entire log of this thread
//assert(globalComparisonIDs.size() == globalComparisons.size());
/* while(globalComparisons.size()>0) {
comparison* c = globalComparisons.back();
globalComparisons.pop_back();
// Delete this tag only if it has not already been destroyed by the abort algorithm
if(!c->isDestroyed()) delete c;
modularApp* ma = globalModularApps.back();
globalModularApps.pop_back();
// Delete this tag only if it has not already been destroyed by the abort algorithm
if(!ma->isDestroyed()) delete ma;
}*/
/*for(list<comparison*>::const_reverse_iterator i=globalComparisons.rbegin();
i!=globalComparisons.rend(); ++i) {
// If this comparison object has not yet been destroyed (possible if we're
// finalizing while the application is being explicitly exited or aborted)
//if(!(*i)->isDestroyed())
delete *i;
}*/
// cout << ">>>SightThreadFinalize"<<endl;
}
pthread_mutex_unlock(&threadSoStacksMutex);
}
void killOtherThreads() {
pthread_mutex_lock(&threadSoStacksMutex);
static bool killPerformed=false;
// cout << pthread_self()<<": killOtherThreads() killPerformed="<<killPerformed<<endl;
if(!killPerformed) {
// Set killPerformed to true to ensure that no other thread will start killing
// threads and immediately give up the lock
killPerformed=true;
pthread_mutex_unlock(&threadSoStacksMutex);
// cout << pthread_self()<<": killOtherThreads() #threadSoStacks="<<threadSoStacks.size()<<endl;
for(std::map<pthread_t, map<dbgStream*, std::list<sightObj*> >* >::iterator t=threadSoStacks.begin();
t!=threadSoStacks.end(); t++) {
if(t->first!=pthread_self()) {
// cout << pthread_self()<<": killOtherThreads() Killing thread "<<t->first<<endl;
/* // Let go of threadSoStacksMutex so that the thread we're about to kill may grab it.
// The killed thread will call killOtherThreads() but isnce killPerformed is already
// ==true, it will exit immediately and release threadSoStacksMutex.
*/
int ret = pthread_cancel(t->first);
if(ret == ESRCH) { cerr << "ERROR canceling thread "<<t->first<<"!"<<endl; }
//pthread_join(t->first, NULL);
// Re-acquire
//pthread_mutex_lock(&threadSoStacksMutex);
//sleep(1);
//sightObj::destroyAll(t->second);
}
}
// Give other threads time to shut down. This is a workaround for the case where we
// cancel a thread but it continues to live.
sleep(1);
} else
pthread_mutex_unlock(&threadSoStacksMutex);
}
/*void SightInit_internal(properties* props, bool storeProps)
{
properties::iterator sightIt = props->find("sight");
assert(!sightIt.isEnd());
// Create the directory structure for the structural information
// Main output directory
createDir(properties::get(sightIt, "workDir"), "");
// Directory where client-generated images will go
string imgDir = createDir(properties::get(sightIt, "workDir"), "html/dbg_imgs");
// Directory that widgets can use as temporary scratch space
string tmpDir = createDir(properties::get(sightIt, "workDir"), "html/tmp");
// Record that we've initialized the main thread as well as this thread.
// This function is used during merging, so there should only be one thread.
initializedDebugMainThread = true;
initializedDebugThisThread = true;
dbg->init(storeProps? props: NULL, properties::get(sightIt, "title"), properties::get(sightIt, "workDir"), imgDir, tmpDir);
}*/
// Creates a new dbgStream based on the given properties of a "sight" tag and returns a pointer to it.
// storeProps: if true, the given properties object is emitted into this dbgStream's output file
structure::dbgStream* createDbgStream(properties* props, bool storeProps) {
properties::iterator sightIt = props->find("sight");
assert(!sightIt.isEnd());
// Create the directory structure for the structural information
// Main output directory
createDir(properties::get(sightIt, "workDir"), "");
// Directory where client-generated images will go
string imgDir = createDir(properties::get(sightIt, "workDir"), "html/dbg_imgs");
// Directory that widgets can use as temporary scratch space
string tmpDir = createDir(properties::get(sightIt, "workDir"), "html/tmp");
return new dbgStream(storeProps? props: NULL, properties::get(sightIt, "title"), properties::get(sightIt, "workDir"), imgDir, tmpDir);
}
// Empty implementation of Sight initialization, to be used when Sight is disabled via #define DISABLE_SIGHT
void NullSightInit(std::string title, std::string workDir) {}
void NullSightInit(int argc, char** argv, std::string title, std::string workDir) {}
/**********************
***** Call Paths *****
**********************/
#if (CALLPATH_ENABLED==1)
ThreadLocalStorage0<CallpathRuntime> CPRuntime;
string cp2str(const Callpath& cp) {
ostringstream s;
//const_cast<Callpath&>(cp).write_out(s);
//Callpath cp2 = cp;
//cp2.write_out(s);
s << cp;
//cout << "callpath: "<<s.str()<<endl<<cp<<endl;
return s.str();
}
/*Callpath str2cp(string str) {
istringstream s(str);
return Callpath::read_in(s);
}*/
#endif // CALLPATH_ENABLED
/********************
***** location *****
********************/
location::location()
{
// Initialize fileLevel with a 0 to make it possible to count top-level files
l.push_back(make_pair(0, list<int>(1, 0)));
}
location::~location()
{
// If Sight has already been destroyed, skip out of the destructor
if(sightObj::SightDestroyed) return;
assert(l.size()==1);
l.pop_back();
}
void location::enterFileBlock() {
assert(l.size()>0);
// Increment the index of this file unit within the current nesting level
(l.back().first)++;
// Add a fresh file level to the location
l.push_back(make_pair(0, list<int>(1, 0)));
l.push_back(make_pair(0, list<int>()));
}
void location::exitFileBlock() {
assert(l.size()>0);
l.pop_back();
}
void location::enterBlock() {
assert(l.size()>0);
assert(l.back().second.size()>0);
// Increment the index of this block unit within the current nesting level of this file
l.back().second.back()++;
// Add a new level to the block list, starting the index at 0
l.back().second.push_back(0);
}
void location::exitBlock() {
assert(l.size()>0);
assert(l.back().second.size()>0);
l.back().second.pop_back();
}
void location::operator=(const location& that)
{ l = that.l; }
bool location::operator==(const location& that) const
{ return l==that.l; }
bool location::operator!=(const location& that) const
{ return l!=that.l; }
bool location::operator<(const location& that) const
{ return l<that.l; }
//void location::print(std::ofstream& ofs) const {
std::string location::str(std::string indent) const {
ostringstream ofs;
ofs << "[location: "<<endl;
for(std::list<std::pair<int, std::list<int> > >::const_iterator i=l.begin(); i!=l.end(); i++) {
ofs << " "<<i->first<<" :";
for(std::list<int>::const_iterator j=i->second.begin(); j!=i->second.end(); j++) {
ofs << " "<<*j;
}
ofs << endl;
}
ofs << "]";
return ofs.str();
}
/********************
***** sightObj *****
********************/
// Records whether we've begun the process of destroying all objects. This is to differentiate`
// the case of Sight doing the destruction and of the runtime system destroying all objects
// at process termination
ThreadLocalStorage1<bool, bool> sightObj::SightDestruction(false);
// Records whether we've completed the process of destroying all objects
ThreadLocalStorage1<bool, bool> sightObj::SightDestroyed(false);
// Records whether we've started processing static destructors. In this case
// the sight object stack may not be valid anymore and thus should not be accessed.
ThreadLocalStorage1<bool, bool> sightObj::stackMayBeInvalidFlag(false);
// The of clocks currently being used, mapping the name of each clock class to the set of active
// clock objects of this class.
//ThreadLocalStorage0<std::map<std::string, std::set<sightClock*> > > sightObj::clocks;
ThreadLocalStorageMap<std::string, std::set<sightClock*> > sightObj::clocks;
sightObj::sightObj(dbgStream* outStream) :
props(NULL), emitExitTag(false), destroyed(false), outStream(outStream?outStream:structure::dbg.get()) {
// Push this sightObj onto the stack
//if(initializedDebug) soStack.push_back(this);
// Don't push this sightObj onto the stack because emitExitTag is initialized to false
//soStack(this->outStream);
}
// isTag - if true, we emit a single enter/exit tag combo in the constructor and nothing in the destructor
sightObj::sightObj(properties* props, bool isTag, dbgStream* outStream) :
props(props), destroyed(false), outStream(outStream?outStream:structure::dbg.get()) {
init(props, isTag);
}
void sightObj::init(properties* props, bool isTag) {
// If Sight has already been initialized on the main thread but not on this thread, initialize it now
if(initializedDebugMainThread && !initializedDebugThisThread) SightInit_NewThread();
assert(outStream);
// cout << "sightObj::sightObj isTag="<<isTag<<" props="<<(props? props->str(): "NULL")<<endl;
if(props && props->active && props->emitTag) {
// Add the properties of any clocks associated with this sightObj
for(map<string, set<sightClock*> >::iterator i=clocks.begin(); i!=clocks.end(); i++) {
for(set<sightClock*>::iterator j=i->second.begin(); j!=i->second.end(); j++)
// If the value of the current clock was modified since the last time we observed it
//if((*j)->modified())
(*j)->setProperties(props);
}
if(isTag) {
outStream->tag(this);
emitExitTag = false;
} else {
outStream->enter(this);
emitExitTag = true;
}
} else
emitExitTag = false;
// Push this sightObj onto the stack if Sight has already been initialized on the main thread
if(initializedDebugMainThread && emitExitTag) {
// cout << "[[[ "<<(props? props->str(): "NULL")<<endl;
soStack(outStream).push_back(this);
}
// If Sight was not initialized at the time this object was created, record that
// this object does not emit an exit tag since this object should be left invisible to Sight.
if(!initializedDebugMainThread) emitExitTag = false;
// Initially removeFromStack is identical to emitExitTag but if we call emitTag()
// before the object is destructed, it will become true while emitExitTag is set to false.
removeFromStack = emitExitTag;
}
// Emits this sightObj's exit tag to the outgoing stream
// If popStack==true, remove this sightObj from the stack of objects on its dbgStream.
// It will be set to false inside the dbgStream constructor since 1. its redundant and
// 2. if the dbgStream is the static one, the stack may have been destructed before the dbgStream's destructor
void sightObj::exitTag(bool popStack) {
assert(outStream);
if(!emitExitTag) return;
outStream->exit(this);
// Pop this sightObj off the top of the stack
// The stack is empty when we're trying to destroy global/static sightObjs created before Sight was initialized
if(popStack)
if(soStack(this->outStream).size()>0) {
// Remove this object from the stack
assert(soStack(this->outStream).back() == this);
soStack(this->outStream).pop_back();
}
emitExitTag = false;
}
// Directly calls the destructor of this object. This is necessary because when an application crashes
// Sight must clean up its state by calling the destructors of all the currently-active sightObjs. Since
// there is no way to directly call the destructor of a given object when it may have several levels
// of inheritance above sightObj, each object must enable Sight to directly call its destructor by calling
// it inside the destroy() method. The fact that this method is virtual ensures that calling destroy() on
// an object will invoke the destroy() method of the most-derived class.
void sightObj::destroy() {
this->~sightObj();
}
sightObj::~sightObj() {
//assert(!destroyed);
if(destroyed) return;
assert(outStream);
/* if(soStack(outStream).size()>0 && emitExitTag) {
cout << "]]](#"<<soStack(outStream).size()<<") props="<<(props? props->str(): "NULL")<<endl;
cout << "soStack(outStream).back()="<<(soStack(outStream).back()->props? soStack(outStream).back()->props->str(): "NULL")<<endl;
}*/
// If the application calls to exit(), this will call the destructors of all the static objects
// but not those on the stack or heap. As such, it is possible to reach the destructor of an object
// in the middle of the soStack without calling the destructors for objects in the middle.
// As such, we call their destructors directly right now.
/* while(emitExitTag && soStack(outStream).back() != this)
soStack(outStream).back()->destroy();*/
// Pop this sightObj off the top of the stack
// The stack is empty when we're trying to destroy global/static sightObjs created before Sight was initialized
if(!stackMayBeInvalidFlag && removeFromStack && soStack(outStream).size()>0) {
// Remove this object from the stack
if(soStack(outStream).back() != this) {
cerr << "ERROR: attempting to exit an object that is not the most recent on the stack!"<<endl;
cerr << "Exiting: "<<(props? props->str(): "NULL")<<endl;
cerr << "Stack:"<<endl;
for(list<sightObj*>::reverse_iterator i=soStack(outStream).rbegin(); i!=soStack(outStream).rend(); ++i) {
if((*i)->props) cerr << ": "<<(*i)->props->str(": ")<<endl;
else cerr << ": NULL"<<endl;
}
//if(soStack(outStream).back()->props) cerr << "Top of stack: "<<soStack(outStream).back()->props->str()<<endl;
}
assert(soStack(outStream).back() == this);
soStack(outStream).pop_back();
}
//assert(props);
if(props) {
//cout << "sightObj::~sightObj(), emitExitTag="<<emitExitTag<<" props="<<props->str()<<endl;
if(props->active && props->emitTag && emitExitTag)
outStream->exit(this);
delete props;
props = NULL;
}
// We've finished destroying this object and it should not be destroyed again,
// even if the destructor is explicitly called.
destroyed = true;
// Invoke the callbacks registered by classes that derive from sightObj to notify them that the destruction of this
// sightObj has completed.
for(list<destructNotifier>::iterator n=sightObjDestructNotifiers.begin(); n!=sightObjDestructNotifiers.end(); n++)
(*n)(this);
sightObjDestructNotifiers.clear();
}
// Destroy all the currently live sightObjs on the stack
void sightObj::destroyAll(map<dbgStream*, list<sightObj*> >* stack) {
// Record that we've begun the process of destroying all sight objects
SightDestruction = true;
// Call the destroy method of each object on the soStack
// map<dbgStream*, list<sightObj*> >& stack = soStackAllStreams();
if(stack==NULL) stack = &soStackAllStreams();
assert(stack);
// cout << pthread_self()<<": sightObj::destroyAll() <<<< stack="<<stack<<", #stack="<<stack->size()<<endl;
for(map<dbgStream*, list<sightObj*> >::iterator s=stack->begin(); s!=stack->end(); s++) {
while(s->second.size()>0) {
list<sightObj*>::reverse_iterator o=s->second.rbegin();
// if((*o)->emitExitTag) {
if((*o)->props) {
// cout << ">!> "<<(*o)->props->name()<<endl;
assert(!stackMayBeInvalidFlag);
(*o)->destroy();
} else {
// cout << ">!> NULL"<<endl;
s->second.pop_back();
}
// }
// The call to destroy will remove the last element from soStack(outStream)
}
}
stack->clear();
// cout << pthread_self()<<": sightObj::destroyAll() >>>>"<<endl;
// Sight has now been destroyed
SightDestroyed = true;
}
// Destroy all the currently live sightObjs on the current thread's stack, down to
// the given stack depth (the given number of sightObjs will be left on the stack
// at the end of destroyThreadStack())
void sightObj::destroyThreadStack(int targetStackDepth) {
// Record that we've begun the process of destroying all sight objects
SightDestruction = true;
// Call the destroy method of each object on this thread's soStack in reverse order
list<sightObj*>& stack = soStack(dbg);
while(stack.size()>targetStackDepth) {
list<sightObj*>::reverse_iterator o=stack.rbegin();
if((*o)->props) {
// cout << ">!> "<<(*o)->props->name()<<endl;
assert(!stackMayBeInvalidFlag);
(*o)->destroy();
} else {
// cout << ">!> NULL"<<endl;
stack.pop_back();
}
// The call to destroy will remove the last element from soStack(outStream)
}
// Indicate that we've completed the destruction process
SightDestruction = false;
}
// Returns whether this object is active or not
bool sightObj::isActive() const {
assert(props);
return props->active;
}
// Registers a new clock with sightObj
void sightObj::addClock(std::string clockName, sightClock* c) {
// This clockName/clock object combination does not currently exist in clocks
assert(clocks.find(clockName) == clocks.end() ||
(clocks.find(clockName) != clocks.end() && clocks[clockName].find(c) == clocks[clockName].end()));
clocks[clockName].insert(c);
}
// Updates the registration of the given clock to refer to the given sightClock object
void sightObj::updClock(std::string clockName, sightClock* c) {
// This clockName/clock object combination must currently exist in clocks
assert(clocks.find(clockName) != clocks.end());
assert(clocks[clockName].find(c) != clocks[clockName].end());
clocks[clockName].insert(c);
}
// Unregisters the clock with the given name
void sightObj::remClock(std::string clockName) {
// This clockName/clock object combination must currently exist in clocks
assert(clocks.find(clockName) != clocks.end());
clocks.erase(clockName);
}
// Returns whether the given clock object is currently registered
bool sightObj::isActiveClock(std::string clockName, sightClock* c) {
return clocks.find(clockName) != clocks.end() &&
clocks[clockName].find(c) != clocks[clockName].end();
}
// Registers a given callback function to be called when the destruction of this object completes.
void sightObj::registerDestructNotifier(destructNotifier notifier) {
sightObjDestructNotifiers.push_back(notifier);
}
/************************************
***** MergeHandlerInstantiator *****
************************************/
std::map<std::string, MergeHandler>* MergeHandlerInstantiator::MergeHandlers;
std::map<std::string, MergeKeyHandler>* MergeHandlerInstantiator::MergeKeyHandlers;
std::set<GetMergeStreamRecord>* MergeHandlerInstantiator::MergeGetStreamRecords;
MergeHandlerInstantiator::MergeHandlerInstantiator() :