HPCToolkit
hpcrun_dlfns.c
Go to the documentation of this file.
1 // -*-Mode: C++;-*- // technically C99
2 
3 // * BeginRiceCopyright *****************************************************
4 //
5 // $HeadURL$
6 // $Id$
7 //
8 // --------------------------------------------------------------------------
9 // Part of HPCToolkit (hpctoolkit.org)
10 //
11 // Information about sources of support for research and development of
12 // HPCToolkit is at 'hpctoolkit.org' and in 'README.Acknowledgments'.
13 // --------------------------------------------------------------------------
14 //
15 // Copyright ((c)) 2002-2019, Rice University
16 // All rights reserved.
17 //
18 // Redistribution and use in source and binary forms, with or without
19 // modification, are permitted provided that the following conditions are
20 // met:
21 //
22 // * Redistributions of source code must retain the above copyright
23 // notice, this list of conditions and the following disclaimer.
24 //
25 // * Redistributions in binary form must reproduce the above copyright
26 // notice, this list of conditions and the following disclaimer in the
27 // documentation and/or other materials provided with the distribution.
28 //
29 // * Neither the name of Rice University (RICE) nor the names of its
30 // contributors may be used to endorse or promote products derived from
31 // this software without specific prior written permission.
32 //
33 // This software is provided by RICE and contributors "as is" and any
34 // express or implied warranties, including, but not limited to, the
35 // implied warranties of merchantability and fitness for a particular
36 // purpose are disclaimed. In no event shall RICE or contributors be
37 // liable for any direct, indirect, incidental, special, exemplary, or
38 // consequential damages (including, but not limited to, procurement of
39 // substitute goods or services; loss of use, data, or profits; or
40 // business interruption) however caused and on any theory of liability,
41 // whether in contract, strict liability, or tort (including negligence
42 // or otherwise) arising in any way out of the use of this software, even
43 // if advised of the possibility of such damage.
44 //
45 // ******************************************************* EndRiceCopyright *
46 
47 //***************************************************************************
48 //
49 // File:
50 // $HeadURL$
51 //
52 // Purpose:
53 // [...]
54 //
55 // Description:
56 // [The set of functions, macros, etc. defined in the file]
57 //
58 // Author:
59 // [...]
60 //
61 //***************************************************************************
62 
63 #include "hpcrun_dlfns.h"
64 #include "fnbounds_interface.h"
65 #include "sample_event.h"
66 #include "thread_data.h"
67 
68 #include <messages/messages.h>
69 #include <lib/prof-lean/spinlock.h>
70 #include <monitor.h>
71 
72 
73 //
74 // Protect dlopen(), dlclose() and dl_iterate_phdr() with a readers-
75 // writers lock. That is, either one writer and no readers, or else
76 // no writers and any number of readers.
77 //
78 // dlopen and dlclose modify the internal dl data structures, so
79 // they're writers, dl_iterate_phdr (via sampling or pthread_create)
80 // is a reader. Note: this lock needs to be process-wide.
81 //
82 // Now allow a writer to lock against itself. That is, we record the
83 // thread id of the writer and if a nested dlopen (from init ctor)
84 // happens in the same thread, then we allow the thread to proceed.
85 // Normally, this would be dangerous (exposes inconsistent state).
86 // But in this case, the init ctor occurs after the dangerous part.
87 // This is necessary to handle nested dlopens in an init constructor.
88 //
89 // And if we could just separate dlopen() into mmap() and its init
90 // constructor, then we'd only need to block the mmap part. :-(
91 //
92 // DLOPEN_RISKY disables the read locks (always succeed), so that
93 // sampling will never be blocked in this case. But we keep the write
94 // locks for the benefit of the fnbounds functions.
95 //
97 static atomic_long dlopen_num_readers = ATOMIC_VAR_INIT(0);
98 static volatile long dlopen_num_writers = 0;
99 static int dlopen_writer_tid = -1;
100 static atomic_long num_dlopen_pending = ATOMIC_VAR_INIT(0);
101 
102 
103 // We use this only in the DLOPEN_RISKY case.
104 long
106 {
108 }
109 
110 
111 // Writers always wait until they acquire the lock. Now allow writers
112 // to lock against themselves, but only in the same thread.
113 static void
115 {
116  int tid = monitor_get_thread_num();
117  int acquire = 0;
118 
119  do {
120  spinlock_lock(&dlopen_lock);
121  if (dlopen_num_writers == 0 || tid == dlopen_writer_tid) {
124  acquire = 1;
125  }
126  spinlock_unlock(&dlopen_lock);
127  } while (! acquire);
128 
129  // Wait for any readers to finish.
130  if (! ENABLED(DLOPEN_RISKY)) {
132  }
133 }
134 
135 
136 static void
138 {
140 }
141 
142 
143 // Downgrade the dlopen lock from a writers lock to a readers lock.
144 // Must already hold the writers lock.
145 static void
147 {
149  dlopen_num_writers = 0;
150 }
151 
152 
153 // Readers try to acquire a lock, but they don't wait if that fails.
154 // Returns: 1 if acquired, else 0 if not.
155 int
157 {
158  int acquire = 0;
159 
160  spinlock_lock(&dlopen_lock);
161  if (dlopen_num_writers == 0 || ENABLED(DLOPEN_RISKY)) {
163  acquire = 1;
164  }
165  spinlock_unlock(&dlopen_lock);
166 
167  return (acquire);
168 }
169 
170 
171 void
173 {
175 }
176 
177 
178 void
179 hpcrun_pre_dlopen(const char *path, int flags)
180 {
183  TD_GET(inside_dlfcn) = true;
184 }
185 
186 
187 // It's safe to downgrade the lock during fnbounds_map_open_dsos()
188 // because it acquires the dl-iterate lock before the fnbounds lock,
189 // and that order is consistent with sampling. Note: can only
190 // downgrade the lock on the last (outermost) dlopen.
191 //
192 void
193 hpcrun_dlopen(const char *module_name, int flags, void *handle)
194 {
195  int outermost = (dlopen_num_writers == 1);
196 
197  TMSG(LOADMAP, "dlopen: handle = %p, name = %s", handle, module_name);
198  if (outermost) {
200  }
203  if (outermost) {
204  TD_GET(inside_dlfcn) = false;
206  } else {
208  }
209 }
210 
211 
212 void
213 hpcrun_dlclose(void *handle)
214 {
216  TD_GET(inside_dlfcn) = true;
217 }
218 
219 
220 // We can't downgrade the lock during fnbounds_unmap_closed_dsos()
221 // because it acquires the fnbounds lock before the dl-iterate lock,
222 // and that is a LOR with sampling.
223 //
224 void
225 hpcrun_post_dlclose(void *handle, int ret)
226 {
227  int outermost = (dlopen_num_writers == 1);
228 
229  TMSG(LOADMAP, "dlclose: handle = %p", handle);
231  if (outermost) {
232  TD_GET(inside_dlfcn) = false;
233  }
235 }
long hpcrun_dlopen_pending(void)
Definition: hpcrun_dlfns.c:105
void hpcrun_dlopen_read_unlock(void)
Definition: hpcrun_dlfns.c:172
#define atomic_fetch_add_explicit(object, operand, order)
Definition: stdatomic.h:366
#define ATOMIC_VAR_INIT(value)
Definition: stdatomic.h:132
static void spinlock_unlock(spinlock_t *l)
Definition: spinlock.h:96
static volatile long dlopen_num_writers
Definition: hpcrun_dlfns.c:98
void hpcrun_pre_dlopen(const char *path, int flags)
Definition: hpcrun_dlfns.c:179
static __thread u32 tid
void fnbounds_map_open_dsos()
#define atomic_load_explicit(object, order)
Definition: stdatomic.h:378
static int dlopen_writer_tid
Definition: hpcrun_dlfns.c:99
void fnbounds_unmap_closed_dsos()
int hpcrun_dlopen_read_lock(void)
Definition: hpcrun_dlfns.c:156
static void hpcrun_dlopen_write_unlock(void)
Definition: hpcrun_dlfns.c:137
void hpcrun_post_dlclose(void *handle, int ret)
Definition: hpcrun_dlfns.c:225
static atomic_long dlopen_num_readers
Definition: hpcrun_dlfns.c:97
static void hpcrun_dlopen_downgrade_lock(void)
Definition: hpcrun_dlfns.c:146
#define TD_GET(field)
Definition: thread_data.h:256
static void spinlock_lock(spinlock_t *l)
Definition: spinlock.h:111
static spinlock_t dlopen_lock
Definition: hpcrun_dlfns.c:96
#define TMSG(f,...)
Definition: messages.h:93
static void hpcrun_dlopen_write_lock(void)
Definition: hpcrun_dlfns.c:114
void hpcrun_dlopen(const char *module_name, int flags, void *handle)
Definition: hpcrun_dlfns.c:193
#define SPINLOCK_UNLOCKED
Definition: spinlock.h:84
static atomic_long num_dlopen_pending
Definition: hpcrun_dlfns.c:100
#define ENABLED(f)
Definition: debug-flag.h:76
void hpcrun_dlclose(void *handle)
Definition: hpcrun_dlfns.c:213