/
omrintrospect.c
1250 lines (1094 loc) · 42 KB
/
omrintrospect.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 IBM Corp. and others 1991
*
* This program and the accompanying materials are made available under
* the terms of the Eclipse Public License 2.0 which accompanies this
* distribution and is available at https://www.eclipse.org/legal/epl-2.0/
* or the Apache License, Version 2.0 which accompanies this distribution and
* is available at https://www.apache.org/licenses/LICENSE-2.0.
*
* This Source Code may also be made available under the following
* Secondary Licenses when the conditions for such availability set
* forth in the Eclipse Public License, v. 2.0 are satisfied: GNU
* General Public License, version 2 with the GNU Classpath
* Exception [1] and GNU General Public License, version 2 with the
* OpenJDK Assembly Exception [2].
*
* [1] https://www.gnu.org/software/classpath/license.html
* [2] https://openjdk.org/legal/assembly-exception.html
*
* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 OR GPL-2.0-only WITH OpenJDK-assembly-exception-1.0
*******************************************************************************/
/**
* @file
* @ingroup Port
* @brief process introspection support
*/
#include <pthread.h>
#include <ucontext.h>
#include "sys/sigcontext.h"
#include <sched.h>
#include <string.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <errno.h>
#include <stdio.h>
#include <poll.h>
#include <time.h>
#include <fcntl.h>
#include <dirent.h>
#define __USE_GNU 1
#include <dlfcn.h>
#include <sys/utsname.h>
#include <tpf/tpfapi.h>
#include <tpf/c_thgl.h>
#include <tpf/c_cinfc.h>
/* z/TPF doesn't have a fdatasync(), it takes */
/* one argument and returns void. Nullify it.*/
#define fdatasync(x) {}
#include "omrintrospect.h"
#include "omrport.h"
#include "omrportpriv.h"
#include "omrportpg.h"
#include "omrsignal_context.h"
#include "omrutilbase.h"
#include "ut_omrport.h"
extern void loadfpc();
/*
* This is the signal that we use to suspend the various threads. This signal must be not be collapsable and
* must pass on values specified by sigqueue.
*/
#define SUSPEND_SIG SIGUSR1
/*
* Internal semaphore retry timeouts. Some implementations of poll seem unreliable so we add a
* timeout to poll calls whether or not we have a deadline so we have a sample based scheduler visible
* spinlock.
*/
#define POLL_RETRY_INTERVAL -1
typedef struct {
int descriptor_pair[2];
volatile uintptr_t in_count;
volatile uintptr_t out_count;
uintptr_t initial_value;
uintptr_t spinlock;
volatile uintptr_t released;
} barrier_r;
typedef struct {
int descriptor_pair[2];
volatile uintptr_t sem_value;
volatile uintptr_t initial_value;
uintptr_t spinlock;
} sem_t_r;
/*
* This structure contains platform specific information necessary to iterate over the threads
*/
struct PlatformWalkData {
/* thread to filter out of the platform iterator */
uintptr_t filterThread;
/* controller thread */
uintptr_t controllerThread;
/* volatile error flag for upcall handlers, shared across threads, non-zero means error */
volatile unsigned char error;
/* is the state currently consistent. False inside startDo/nextDo, must be true otherwise */
volatile unsigned char consistent;
/* old signal handler for the suspend signal */
struct sigaction oldHandler;
/* old mask */
sigset_t old_mask;
/* backpointer to encapsulating state */
J9ThreadWalkState *state;
/* total number of threads in the process, including calling thread */
long threadCount;
/* suspended threads unharvested */
int threadsOutstanding;
/* thread we're constructing */
J9PlatformThread *thread;
/* platform allocated context, or program allocated context */
unsigned char platformAllocatedContext;
/* records whether we need to clean up in resume */
unsigned char cleanupRequired;
/* the semaphore all suspended threads wait on */
sem_t_r client_sem;
/* the semaphore the controller thread waits on */
sem_t_r controller_sem;
/* barrier to prevent any threads exiting the introspect logic until all are ready */
barrier_r release_barrier;
};
/* Wrapper for the close function that retries on EINTR. */
static int
close_wrapper(int fd)
{
int rc = 0;
if (fd != -1) {
do {
rc = close(fd);
} while ((0 != rc) && (EINTR == errno));
}
return rc;
}
/*
* This function constructs a barrier for use within signal handler contexts. It's a combination o
* spinlock techniques modified with blocking read/write/poll scheduler visible calls to avoid
* thread contention.
*
* @param barrier the barrier structure to initialize
* @param value the number of entrants to expect
*
* @return 0 on success, -1 otherwise
*/
static int
barrier_init_r(barrier_r *barrier, int value)
{
uintptr_t old_value;
memset(barrier, 0, sizeof(barrier_r));
if (pipe(barrier->descriptor_pair) != 0) {
return -1;
}
do {
old_value = barrier->initial_value;
} while (compareAndSwapUDATA((uintptr_t *)&barrier->initial_value, old_value, value) != old_value);
do {
old_value = barrier->in_count;
} while (compareAndSwapUDATA((uintptr_t*)&barrier->in_count, old_value, value) != old_value);
do {
old_value = barrier->released;
} while (compareAndSwapUDATA((uintptr_t*)&barrier->released, old_value, 0) != old_value);
return 0;
}
/*
* This function is a utility function used by the blocking barrier functions to wait in a polite, scheduler
* visible fashion until either prompted or the deadline has expired.
*
* @param barrier the barrier we're operating on
* @param deadline the system time in seconds after which we should return regardless of prompting
*
* @return 0 if cleanly prompted, -1 if timed out, other values are various errors
*/
static int
barrier_block_until_poked(barrier_r *barrier, uintptr_t deadline)
{
int ret = 0;
struct pollfd fds[1];
int interval = POLL_RETRY_INTERVAL;
struct timespec spec;
fds[0].fd = barrier->descriptor_pair[0];
fds[0].events = POLLHUP|POLLERR|POLLNVAL|POLLIN;
fds[0].revents = 0;
if (deadline == 0) {
/* block indefinitely */
interval = POLL_RETRY_INTERVAL;
} else {
/* calculate the interval */
if (clock_gettime(CLOCK_REALTIME, &spec) == -1) {
interval = 0;
} else {
interval = (deadline - spec.tv_sec) * 1000;
/* ensure that the poll timeout is set to the lesser of the retry interval or the deadline */
if (interval < 0) {
/* deadline has expired so run through once then bail at the end */
interval = 0;
} else if (interval > POLL_RETRY_INTERVAL && POLL_RETRY_INTERVAL != -1) {
interval = POLL_RETRY_INTERVAL;
}
}
}
/* wait for something to change */
ret = poll(fds, 1, interval);
if ((ret == -1 && errno != EINTR) || fds[0].revents & (POLLHUP|POLLERR|POLLNVAL)) {
return -2;
}
if (deadline != 0) {
/* check if we've timed out. Do this last as the other possibilities for return are of more interest */
if (clock_gettime(CLOCK_REALTIME, &spec) == -1) {
return -3;
}
if (spec.tv_sec >= deadline) {
return -1;
}
}
return 0;
}
/*
* This function is called by the conductor of the barrier. It will block until all entrants have entered
* the barrier via barrier_enter_r. If a maximum amount of time to block for is specified then this will
* time out once that deadline has expired.
*
* @param barrier the barrier to release
* @param seconds the maximum number of second for which to block. Indefinitely if 0 is specified.
*
* @return 0 on clean release, other values are various errors
*/
static int
barrier_release_r(barrier_r *barrier, uintptr_t seconds)
{
char byte = 0;
int result = 0;
int deadline = 0;
struct timespec spec;
if (clock_gettime(CLOCK_REALTIME, &spec) == -1) {
seconds = 0;
} else {
deadline = spec.tv_sec + seconds;
}
if ((compareAndSwapUDATA((uintptr_t*)&barrier->released, 0, 1)) != 0) {
/* barrier->released should have been 0, write something into the pipe so that poll doesn't block,
* converts this to a busy wait
*/
barrier->released = 1;
write(barrier->descriptor_pair[1], &byte, 1);
}
/* wait until all entrants have arrived */
while ((compareAndSwapUDATA((uintptr_t*)&barrier->in_count, -1, -1)) > 0) {
if ((result = barrier_block_until_poked(barrier, deadline)) == -1) {
/* timeout or error */
break;
}
}
write(barrier->descriptor_pair[1], &byte, 1);
fdatasync(barrier->descriptor_pair[1]);
return result;
}
/*
* This function is called by entrants to the barrier. It will block until all expected entrants have called this
* function and barrier_release_r has been called, or the deadline has expired.
*
* @param barrier the barrier to enter
* @param deadline the system time in seconds after which we should unblock
*
* @return 0 on clean release, other values indicate errors
*/
static int
barrier_enter_r(barrier_r *barrier, uintptr_t deadline)
{
uintptr_t old_value = 0;
char byte = 1;
int result = 0;
/* decrement the wait count */
do {
old_value = barrier->in_count;
} while (compareAndSwapUDATA((uintptr_t*)&barrier->in_count, old_value, old_value - 1) != old_value);
if (old_value == 1 && (compareAndSwapUDATA((uintptr_t*)&barrier->released, 0, 0))) {
/* we're the last through the barrier so wake everyone up */
write(barrier->descriptor_pair[1], &byte, 1);
}
/* if we're entering a barrier with a negative count then count us out but we don't need to do anything */
/* wait until we are formally released */
while (compareAndSwapUDATA((uintptr_t*)&barrier->in_count, -1, -1) > 0 || !barrier->released) {
if ((result = barrier_block_until_poked(barrier, deadline)) < 0) {
/* timeout or error */
break;
}
}
/* increment the out count */
do {
old_value = barrier->out_count;
} while (compareAndSwapUDATA((uintptr_t*)&barrier->out_count, old_value, old_value + 1) != old_value);
return result;
}
/*
* This function updates the expected number of clients the barrier is waiting for. If all clients have already entered the barrier
* the the update will fail.
*
* @param barrier the barrier to update
* @param new_value the new number of entrants expected
*
* @return 0 on success, -1 on failure
*/
static int
barrier_update_r(barrier_r *barrier, int new_value)
{
/* we're altering the expected number of entries into the barrier */
uintptr_t old_value;
int difference = new_value - barrier->initial_value;
if (difference == 0) {
return 0;
}
do {
old_value = barrier->in_count;
} while (compareAndSwapUDATA((uintptr_t*)&barrier->in_count, old_value, old_value + difference) != old_value);
if (old_value < 0 || (old_value == 0 && barrier->initial_value != 0)) {
int restore_value = old_value;
/* barrier was already exited, so undo update and return error */
do {
old_value = barrier->in_count;
} while (compareAndSwapUDATA((uintptr_t*)&barrier->in_count, old_value, restore_value) != old_value);
return -1;
} else {
/* we've updated the in_count so update the initial_value */
do {
old_value = barrier->initial_value;
} while (compareAndSwapUDATA((uintptr_t*)&barrier->initial_value, old_value, new_value) != old_value);
/* don't need to notify anyone if updated_value is <= 0 as release will do it when called */
return 0;
}
}
/*
* This function destroys a barrier created by barrier_init_r. It first wakes up any waiters then waits for them
* to exit the barrier before returning if requested.
*
* @param barrier the barrier to destroy
* @int block if non-zero the barrier will block until waiters have exited
*/
static void
barrier_destroy_r(barrier_r *barrier, int block)
{
int current = 0;
int in = 0;
char byte = 1;
/* clean up and wait for all waiters to exit */
write(barrier->descriptor_pair[1], &byte, 1);
close_wrapper(barrier->descriptor_pair[1]);
close_wrapper(barrier->descriptor_pair[0]);
/* decrement the wait count */
if (block) {
do {
current = compareAndSwapUDATA((uintptr_t*)&barrier->out_count, -1, -1);
in = compareAndSwapUDATA((uintptr_t*)&barrier->in_count, -1, -1);
} while (current + in < barrier->initial_value);
}
}
/*
* Constructs a semaphore for use within signal handlers. The semaphore uses a combination of
* spinlock techniques to acquire the lock and blocking read/write/poll calls to prevent massive
* contention between threads.
*
* @param sem the semaphore to acquire
* @param value the initial count for the semaphore
*
* @return 0 on success, -1 otherwise
*/
static int
sem_init_r(sem_t_r *sem, int value)
{
if (pipe(sem->descriptor_pair) != 0) {
return -1;
}
sem->initial_value = value;
sem->sem_value = value;
sem->spinlock = 0;
return 0;
}
/*
* This function attempts to acquire the semaphore. It will block for, at most, the number of
* seconds specified.
*
* @param sem the semaphore to acquire
* @param seconds the maximum number of seconds the block for. Indefinitely if 0 is specified.
*
* @return 0 if semaphore is acquire, < 0 on error, -1 means timeout occured
*/
static int
sem_timedwait_r(sem_t_r *sem, uintptr_t seconds)
{
uintptr_t old_value = -1;
char byte = 0;
int ret = -1;
struct pollfd fds[1];
struct timespec spec;
int deadline = 0;
int interval = seconds;
fds[0].fd = sem->descriptor_pair[0];
fds[0].events = POLLHUP|POLLERR|POLLNVAL|POLLIN;
fds[0].revents = 0;
if (seconds == 0) {
/* block indefinitely */
interval = POLL_RETRY_INTERVAL;
} else {
/* calculate the deadline */
if (clock_gettime(CLOCK_REALTIME, &spec) == -1) {
interval = 0;
} else {
deadline = seconds + spec.tv_sec;
/* ensure that the poll timeout is set to the lesser of the retry interval or the deadline */
if ((seconds * 1000) < POLL_RETRY_INTERVAL || POLL_RETRY_INTERVAL == -1) {
/* poll timeout is specified in milliseconds */
interval = seconds * 1000;
} else {
interval = POLL_RETRY_INTERVAL;
}
}
}
while (1) {
old_value = compareAndSwapUDATA((uintptr_t*)&sem->sem_value, -1, -1);
while (old_value > 0) {
if (compareAndSwapUDATA((uintptr_t*)&sem->sem_value, old_value, old_value - 1) == old_value) {
/* successfully acquired lock */
return 0;
}
old_value = sem->sem_value;
}
/* wait for something to change */
ret = poll(fds, 1, interval);
if ((ret == -1 && errno != EINTR) || fds[0].revents & (POLLHUP|POLLERR|POLLNVAL)) {
return -2;
}
/* consume a byte off the pipe if it wasn't a timeout */
if (ret > 0) {
/* the pipe is configured as non-blocking, waiting is done on the poll call above*/
ret = read(fds[0].fd, &byte, 1);
if (ret == 0) {
/* EOF, pipe has become invalid */
return -4;
}
}
/* check if we've timed out. Do this last as the other possibilities for return are of more interest */
if (ret == 0 && seconds != 0) {
if (clock_gettime(CLOCK_REALTIME, &spec) == -1) {
return -3;
}
if (spec.tv_sec >= deadline) {
return -1;
}
}
}
}
/*
* This function attempts to acquire the semaphore. It will not block if the semaphore isn't available.
* @param sem the semaphore to acquire
*
* @return 0 if the semaphore was acquired, -1 otherwise.
*/
static int
sem_trywait_r(sem_t_r *sem)
{
uintptr_t old_value = 0;
/* try to get the lock */
old_value = compareAndSwapUDATA((uintptr_t*)&sem->sem_value, -1, -1);
while (old_value > 0) {
int value = compareAndSwapUDATA((uintptr_t*)&sem->sem_value, old_value, old_value - 1);
if (value == old_value) {
/* successfully acquired lock */
return 0;
}
/* retry if simply contending rather than failed */
old_value = value;
}
errno = EAGAIN;
return -1;
}
/*
* This function wakes a client or controller thread waiting on the semaphore if any. Otherwise it increments the
* semaphore count and will allow a single subsequent call to semget to succeed.
*
* @param sem the semaphore to post
*
* @return 0 on success, -1 on error
*/
static int
sem_post_r(sem_t_r *sem)
{
uintptr_t old_value;
char byte = 1;
/* release the lock */
do {
old_value = sem->sem_value;
} while (compareAndSwapUDATA((uintptr_t*)&sem->sem_value, old_value, old_value + 1) != old_value);
/* wake up a waiter */
if (write(sem->descriptor_pair[1], &byte, 1) != 1) {
return -1;
}
return 0;
}
/*
* This function destroys a semaphore created with sem_init_r. It first reduces the semaphores capacity to 0 so that
* no more clients can acquire the lock. If something holds the lock then we undo the block and return an error,
* otherwise we close the file descriptors which should wake up any clients waiting on the lock.
*
* @param the semaphore to destroy
*
* @return 0 on success, -1 on error
*/
static int
sem_destroy_r(sem_t_r *sem)
{
uintptr_t old_value;
/* prevent the semaphore from being acquired by subtracting initial value*/
do {
old_value = sem->sem_value;
} while (compareAndSwapUDATA((uintptr_t*)&sem->sem_value, old_value, old_value - sem->initial_value) != old_value);
if (old_value != sem->initial_value) {
/* undo the block and return error */
do {
old_value = sem->sem_value;
} while (compareAndSwapUDATA((uintptr_t*)&sem->sem_value, old_value, old_value + sem->initial_value) != old_value);
/* semaphore is still in use */
return -1;
}
/* close the pipes */
close_wrapper(sem->descriptor_pair[0]);
close_wrapper(sem->descriptor_pair[1]);
return 0;
}
/*
* Utility function to check if we've timed out.
*
* @param deadline system time by which processing should have completed
*
* @return 1 if deadline expired, 0 otherwise
*/
static int
timedOut(uintptr_t deadline)
{
struct timespec spec;
if (clock_gettime(CLOCK_REALTIME, &spec) == -1) {
return 0;
}
return spec.tv_sec >= deadline;
}
/*
* Utility function to calculate remaining time before the deadline elapses.
*
* @param deadline system time by which processing should have completed
*
* @return number of seconds before timeout
*/
static int
timeout(uintptr_t deadline)
{
int secs = 0;
struct timespec spec;
if (clock_gettime(CLOCK_REALTIME, &spec) == -1) {
return 0;
}
secs = deadline - spec.tv_sec;
if (secs > 0) {
return secs;
}
return 0;
}
/*
* Signal handler used to provide preemptive up-call into native threads. We first check that
* the signal we're handling is one that's expected to give a degree of confidence that the
* value we're passed is a legitimate platform data pointer. After that the procedure is simple:
* 1. wait until posted
* 2. publish thread structure with the threads data to platform data.
* 3. generate backtraces (must be done here for linux if using 'backtrace' call, other platforms
* could do this from another thread).
* 4. post the coordinating thread
* 5. wait for release
*
* The wait in step one is a cancellation point. If we wake from that semaphore and an error is noted
* then we skip stack production and wait for release. There's no timeout on the barrier enter as
* that logic should be dealt with by the coordinating thread.
*
* @param signal the number of the signal we receive. Should be SIGRTMIN or equivilent
* @param siginfo contains data about the signal including the platform data pointer provided when queued
* @param context_arg the context of the thread when the signal was dispatched
*/
static void
upcall_handler(int signal, siginfo_t *siginfo, void *context_arg)
{
thread_context *context = (thread_context*)context_arg;
J9ThreadWalkState *state;
struct PlatformWalkData *data = NULL;
int ret = 0;
pid_t pid = getpid();
uintptr_t tid = omrthread_get_ras_tid();
/* check that this signal was queued by this process. */
if (siginfo->si_code != SI_QUEUE || siginfo->si_value.sival_ptr == NULL) {
/* these are not the signals you are looking for */
return;
}
data = (struct PlatformWalkData*)siginfo->si_value.sival_ptr;
state = data->state;
/* ignore the signal if we are the controller thread, or if an error/timeout has already occurred */
if (data->controllerThread == tid || data->error) {
return;
}
/* block until a context is requested, ignoring interrupts */
ret = sem_timedwait_r(&data->client_sem, timeout(data->state->deadline1));
if (ret != 0) {
/* error or timeout in sem_timedwait_r(), set shared error flag and don't do the backtrace */
data->error = ret;
} else if (data->error) {
/* error set by another thread while we were in sem_timedwait_r(), don't do the backtrace */
} else {
/* construct the thread to pass back */
data->thread = state->portLibrary->heap_allocate(state->portLibrary, state->heap, sizeof(J9PlatformThread));
if (data->thread == NULL) {
data->error = ALLOCATION_FAILURE;
} else {
memset(data->thread, 0, sizeof(J9PlatformThread));
data->thread->thread_id = tid;
data->platformAllocatedContext = 1;
data->thread->context = context;
state->portLibrary->introspect_backtrace_thread(state->portLibrary, data->thread, state->heap, NULL);
if (OMR_ARE_NO_BITS_SET(state->options, OMR_INTROSPECT_NO_SYMBOLS)) {
state->portLibrary->introspect_backtrace_symbols_ex(state->portLibrary, data->thread, state->heap, 0);
}
}
}
if (data->error) {
/* error or timeout, exit signal handler without waiting for the controller thread */
return;
}
/* release the controller */
sem_post_r(&data->controller_sem);
/* wait for the controller to close the pipe */
ret = barrier_enter_r(&data->release_barrier, data->state->deadline2);
if (ret != 0) {
/* timeout or error */
data->error = ret;
}
}
/* These functions count the number of threads in the process. This information is necessary so we can queue the
* correct number of signals to suspend all the threads (we might be able to simply call sigqueue until we get a
* queue full error, but then we can't queue additional signals to prompt thread scheduling. Also it may not be
* capped on all systems).
*
* @param data - the platform specific data, ignored
* @return number of threads in the process
*/
static int
count_threads( struct PlatformWalkData *data ) {
struct iproc *pProc;
struct ithgl *pThgl;
if( is_threaded_ecb() ) {
pProc = iproc_process;
pThgl = pProc->iproc_thread_global_data;
return pThgl->active_cnt;
}
return 0;
}
/* This function installs a signal handler then generates signals until all threads in the process bar the calling
* thread are suspended. The signals have to be real-time signals (ie. >= SIGRTMIN) or it's not possible to pass
* a data via sigqueue. Also, non real-time signals of the same value can be collapsed and we require any threads
* scheduled during this suspension to immediately receive a signal, so we need at least one signal per unsuspended
* thread on the queue.
*
* @param data platform specific control data used during suspend/resume
* @return the number of threads suspended
*/
static int
suspend_all_preemptive(struct PlatformWalkData *data)
{
errno = EBADF;
return -1;
}
/*
* Frees anything and everything to do with the scope of the specified thread.
*/
static void
freeThread(J9ThreadWalkState *state, J9PlatformThread *thread)
{
J9PlatformStackFrame *frame;
struct PlatformWalkData *data = (struct PlatformWalkData*)state->platform_data;
if (thread == NULL) {
return;
}
frame = thread->callstack;
while (frame) {
J9PlatformStackFrame *tmp = frame;
frame = frame->parent_frame;
if (tmp->symbol) {
state->portLibrary->heap_free(state->portLibrary, state->heap, tmp->symbol);
tmp->symbol = NULL;
}
state->portLibrary->heap_free(state->portLibrary, state->heap, tmp);
}
if (!data->platformAllocatedContext && thread->context) {
state->portLibrary->heap_free(state->portLibrary, state->heap, thread->context);
}
state->portLibrary->heap_free(state->portLibrary, state->heap, thread);
if (state->current_thread == thread) {
state->current_thread = NULL;
}
}
/*
* This functions resumes all threads suspended by suspend_all_preemptive. The general behaviour
* is to swallow any spurious signals from the queue so they are not delivered after we remove our
* custom handler, the custom handler is removed, then we wake any threads blocking on the relase
* barrier. On AIX we set the signal handler to SIG_IGN before attempting to swallow signals off
* the queue because it seems to be possible for signals to be allocated to threads without yet
* being delivered, preventing that from working. Setting SIG_IGN seems to remove not yet delivered
* signals.
*
* @param data - platform specific control data used during suspend/resume
*/
static void
resume_all_preempted(struct PlatformWalkData *data)
{
sigset_t set;
int clean_release = 0;
struct timespec time_out;
J9ThreadWalkState *state = data->state;
struct OMRPortLibrary *portLibrary = state->portLibrary;
int test_sig = SUSPEND_SIG;
/*
* We skip everything but the semaphores and the process mask if we didn't
* successfully install the signal handlers. Now there are no outstanding
* signals on the queue we can drop the handler
*/
if (data->threadsOutstanding > 0) {
/* inhibit collection of contexts from any of the outstanding threads we release */
data->error = 1;
}
/* allow any outstanding threads to skip the handler. This will cause semwait to return with an error */
close_wrapper(data->client_sem.descriptor_pair[0]);
close_wrapper(data->client_sem.descriptor_pair[1]);
if (data->cleanupRequired) {
struct OMRPortLibrary *portLibrary = data->state->portLibrary;
/* clear out the signal queue so that any undispatched suspend signals are not left lying around */
while (sigpending(&set) == 0 && sigismember(&set, SUSPEND_SIG)) {
/* there is a suspend signal pending so swallow it. It is worth noting that sigwait isn't in the initial
* set of posix signal safe functions, however the fact that it bypasses the installed signal handler is
* too useful it ignore, so we risk it. It could be replaced with sigsuspend and some handler juggling if
* need be but the added complexity isn't worth it until it's shown to be necessary
*/
sigemptyset(&set);
sigaddset(&set, SUSPEND_SIG);
sigwait(&set, &test_sig);
}
/* restore the old signal handler */
if ((data->oldHandler.sa_flags & SA_SIGINFO) == 0 && data->oldHandler.sa_handler == SIG_DFL ) {
/* if there wasn't an old signal handler the set this to ignore. There shouldn't be any suspend signals
* left but better safe than sorry
*/
data->oldHandler.sa_handler = SIG_IGN;
}
sigaction(SUSPEND_SIG, &data->oldHandler, NULL);
/* release the threads from the upcall handler */
clean_release = barrier_release_r(&data->release_barrier, timeout(data->state->deadline2));
/* make sure they've all exited. 1 means we block until all threads that have entered the barrier leave */
barrier_destroy_r(&data->release_barrier, 1);
}
if (data->error) {
/* allow threads in upcall handler to run */
omrthread_yield();
}
/* clean up the semaphores */
sem_destroy_r(&data->client_sem);
sem_destroy_r(&data->controller_sem);
if (data->state->current_thread != NULL) {
freeThread(data->state, data->state->current_thread);
}
/* restore the mask for this thread */
sigprocmask(SIG_SETMASK, &data->old_mask, NULL);
/* clean up the heap */
data->state->portLibrary->heap_free(data->state->portLibrary, data->state->heap, data);
state->platform_data = NULL;
}
int __attribute__((optimize(0)))
OMRZTPF_getcontext( void *region )
{
register char *rgn asm("r2") = (char *)region;
/*
* Because of the way the prologue works, registers 1, 11, and 15 are
* clobbered; only 11 and 15 are recoverable ... 1 is lost forever.
* (( SEE NOTE ABOVE, please. -O3 also clobbers R4 permanently. ))
* This block needs to point at the *caller's* NSI, so R14's contents need
* to replace bytes 8-15 of the PSW, we'll do that too.
*/
asm volatile (
/*
* This section writes the primary ucontext_t block fields the
* user called for, using all 512 bytes of storage we were passed.
* First we set uc_flags & uc_link to zero, as well as the entire
* region occupied by uc_stack.
*/
"xc 0(40,%0),0(%0)\n\t" /* Zeroize uc_flags & uc_link */
/*
* Next portion is the _sigregs (field: uc_mcontext) block
* It starts at offset 0x0028, where the PSW goes. But we need to
* save the caller's registers first thing, so do that first.
*/
"stmg %%r0,%%r10,56(%0)\n\t" /* store grandes 0-10 at +0x0038 for 0x58 bytes */
"mvc 144(48,%0),544(%%r11)\n\t" /* grandes 11-15 at +0x0090 for 0x28 bytes */
"epsw %%r0,%%r1\n\t" /* Get PSW into registers 0 & 1 */
"stg %%r0,40(%0)\n\t" /* Store PSW hi word at +0x0030 for 0x08 bytes */
"lg %%r0,168(%0)\n\t" /* Pick up original R14, and */
"stg %%r0,48(%r0)\n\t" /* replace IC part of PSW with it. */
"stam %%a0,%%a15,184(%0)\n\t" /* Store A0-15 at +0x00B8 for 0x40 bytes */
"stfpc 248(%0)\n\t" /* Store FPC register at +0x00F8 for 4 bytes */
"xc 252(4,%0),252(%0)\n\t" /* Zeroize 4 bytes' alignment padding (+0x0FC for 4) */
"std %%f0,256(%0)\n\t" /* Store FPR0 at +0x0100 for 8 bytes */
"std %%f1,264(%0)\n\t" /* Store FPR1 at +0x0108 for 8 bytes */
"std %%f2,272(%0)\n\t" /* Store FPR2 at +0x0110 */
"std %%f3,280(%0)\n\t" /* Store FPR3 at +0x0118 */
"std %%f4,288(%0)\n\t" /* FPR4 at +0x0120 */
"std %%f5,296(%0)\n\t" /* FPR5 at +0x0128 */
"std %%f6,304(%0)\n\t" /* FPR6 at +0x0130 */
"std %%f7,312(%0)\n\t" /* FPR7 at +0x0138 */
"std %%f8,320(%0)\n\t" /* FPR8 at +0x0140 */
"std %%f9,328(%0)\n\t" /* FPR9 at +0x0148 */
"std %%f10,336(%0)\n\t" /* FPR10 at +0x0150 */
"std %%f11,344(%0)\n\t" /* FPR11 at +0x0158 */
"std %%f12,352(%0)\n\t" /* FPR12 at +0x0160 */
"std %%f13,360(%0)\n\t" /* FPR13 at +0x0168 */
"std %%f14,368(%0)\n\t" /* FPR14 at +0x0170 */
"std %%f15,376(%0)\n\t" /* FPR15 at +0x0178 */
/*
* Type sigset_t is next, field uc_sigmask at +0x0180 for 80,
* which will come from sigprocmask(), following.
*/
: /* no outputs */ /* output constraints would go here */
: "a"(rgn) /* input constraints go here */
: "%r0", "%r1", "%r4", "memory"); /* clobber list goes here */
sigprocmask( SIG_BLOCK, NULL, &((ucontext_t *)region)->uc_sigmask ); /* Get sig masks */
/*
* Total lth = 0x200 (512) bytes. Send it back to the caller
* in the address we were passed. We only need to return an
* int for success (0) or non-zero for failure. The way this
* routine is written, failure is not possible.
*/
return 0;
}
/*
* Sets up the native thread structures including the backtrace. If a context is specified it is used instead of grabbing
* the context for the thread pointed to by state->current_thread.
*
* @param state - state variable for controlling the thread walk
* @param sigContext - context to use in place of live thread context
*
* @return - 0 on success, non-zero otherwise.
*/
static int
setup_native_thread(J9ThreadWalkState *state, thread_context *sigContext, int heapAllocate)
{
struct PlatformWalkData *data = (struct PlatformWalkData*)state->platform_data;
int size = sizeof(thread_context);
if (size < sizeof(ucontext_t)) {
size = sizeof(ucontext_t);
}
if (heapAllocate || sigContext) {
/* allocate the thread container*/
state->current_thread = (J9PlatformThread*)state->portLibrary->heap_allocate(state->portLibrary, state->heap, sizeof(J9PlatformThread));
if (state->current_thread == NULL) {
return -1;
}
memset(state->current_thread, 0, sizeof(J9PlatformThread));
/* allocate space for the copy of the context */
state->current_thread->context = (thread_context*)state->portLibrary->heap_allocate(state->portLibrary, state->heap, size);
if (state->current_thread->context == NULL) {
return -2;
}
memset(state->current_thread->context, 0, size);
/* copy the thread specifics */
state->current_thread->thread_id = data->thread->thread_id;
state->current_thread->process_id = data->thread->process_id;
state->current_thread->callstack = data->thread->callstack;
/* copy the context */
if (sigContext) {
/* we're using the provided context instead of generating it */
memcpy(state->current_thread->context, ((OMRUnixSignalInfo*)sigContext)->platformSignalInfo.context, size);
} else if (state->current_thread->thread_id == omrthread_get_ras_tid()) {
/* return context for current thread */
OMRZTPF_getcontext((ucontext_t*)state->current_thread->context);