Directory: | cvmfs/ |
---|---|
File: | cvmfs/tracer.cc |
Date: | 2025-02-09 02:34:19 |
Exec | Total | Coverage | |
---|---|---|---|
Lines: | 159 | 164 | 97.0% |
Branches: | 103 | 162 | 63.6% |
Line | Branch | Exec | Source |
---|---|---|---|
1 | /** | ||
2 | * This file is part of the CernVM File System. | ||
3 | */ | ||
4 | |||
5 | |||
6 | #include "tracer.h" | ||
7 | |||
8 | #include <pthread.h> | ||
9 | |||
10 | #include <cassert> | ||
11 | #include <cerrno> | ||
12 | #include <cstdio> | ||
13 | #include <cstdlib> | ||
14 | #include <cstring> | ||
15 | #include <string> | ||
16 | |||
17 | #include "util/atomic.h" | ||
18 | #include "util/concurrency.h" | ||
19 | #include "util/posix.h" | ||
20 | #include "util/string.h" | ||
21 | |||
22 | using namespace std; // NOLINT | ||
23 | |||
24 | |||
25 | 14 | void Tracer::Activate( | |
26 | const int buffer_size, | ||
27 | const int flush_threshold, | ||
28 | const string &trace_file) | ||
29 | { | ||
30 | 14 | trace_file_ = trace_file; | |
31 | 14 | buffer_size_ = buffer_size; | |
32 | 14 | flush_threshold_ = flush_threshold; | |
33 |
3/6✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 14 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 14 times.
✗ Branch 5 not taken.
|
14 | assert(buffer_size_ > 1 && flush_threshold_>= 0 |
34 | && flush_threshold_ < buffer_size_); | ||
35 | |||
36 |
4/8✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
✓ Branch 4 taken 4194 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 4194 times.
✓ Branch 7 taken 14 times.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
|
4208 | ring_buffer_ = new BufferEntry[buffer_size_]; |
37 |
1/2✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
|
14 | commit_buffer_ = new atomic_int32[buffer_size_]; |
38 |
2/2✓ Branch 0 taken 4194 times.
✓ Branch 1 taken 14 times.
|
4208 | for (int i = 0; i < buffer_size_; i++) |
39 | 4194 | atomic_init32(&commit_buffer_[i]); | |
40 | |||
41 | int retval; | ||
42 | 14 | retval = pthread_cond_init(&sig_continue_trace_, NULL); | |
43 | 14 | retval |= pthread_mutex_init(&sig_continue_trace_mutex_, NULL); | |
44 | 14 | retval |= pthread_cond_init(&sig_flush_, NULL); | |
45 | 14 | retval |= pthread_mutex_init(&sig_flush_mutex_, NULL); | |
46 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 14 times.
|
14 | assert(retval == 0); |
47 | |||
48 | 14 | active_ = true; | |
49 | 14 | } | |
50 | |||
51 | |||
52 | /** | ||
53 | * Trace a message. This is usually a lock-free procedure that just | ||
54 | * requires two fetch_and_add operations and a gettimeofday syscall. | ||
55 | * There are two exceptions: | ||
56 | * -# If the ring buffer is full, the function blocks until the flush | ||
57 | * thread made some space. Avoid that by carefully choosing size | ||
58 | * and threshold. | ||
59 | * -# If this message reaches the threshold, the flush thread gets | ||
60 | * signaled. | ||
61 | * | ||
62 | * \param[in] event Arbitrary code, for consistency applications should use one | ||
63 | * of the TraceEvents constants. Negative codes are reserved | ||
64 | * for internal use. | ||
65 | * \param[in] id Arbitrary id, for example file name or module name which is | ||
66 | * doing the trace. | ||
67 | * \return The sequence number which was used to trace the record | ||
68 | */ | ||
69 | 345535 | int32_t Tracer::DoTrace( | |
70 | const int event, | ||
71 | const PathString &path, | ||
72 | const string &msg) | ||
73 | { | ||
74 | 345535 | int32_t my_seq_no = atomic_xadd32(&seq_no_, 1); | |
75 | timeval now; | ||
76 | 360571 | gettimeofday(&now, NULL); | |
77 | 360383 | int pos = my_seq_no % buffer_size_; | |
78 | |||
79 |
2/2✓ Branch 1 taken 119351 times.
✓ Branch 2 taken 357143 times.
|
482477 | while (my_seq_no - atomic_read32(&flushed_) >= buffer_size_) { |
80 | timespec timeout; | ||
81 | int retval; | ||
82 | 119351 | GetTimespecRel(25, &timeout); | |
83 | 117370 | retval = pthread_mutex_lock(&sig_continue_trace_mutex_); | |
84 |
1/2✓ Branch 1 taken 124589 times.
✗ Branch 2 not taken.
|
124589 | retval |= pthread_cond_timedwait(&sig_continue_trace_, |
85 | &sig_continue_trace_mutex_, &timeout); | ||
86 | 124589 | retval |= pthread_mutex_unlock(&sig_continue_trace_mutex_); | |
87 |
2/4✓ Branch 0 taken 122288 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 122288 times.
|
122094 | assert(retval == ETIMEDOUT || retval == 0); |
88 | } | ||
89 | |||
90 | 357143 | ring_buffer_[pos].time_stamp = now; | |
91 | 357143 | ring_buffer_[pos].code = event; | |
92 |
1/2✓ Branch 1 taken 344774 times.
✗ Branch 2 not taken.
|
357143 | ring_buffer_[pos].path = path; |
93 |
1/2✓ Branch 1 taken 356486 times.
✗ Branch 2 not taken.
|
344774 | ring_buffer_[pos].msg = msg; |
94 | 356486 | atomic_inc32(&commit_buffer_[pos]); | |
95 | |||
96 |
2/2✓ Branch 1 taken 36539 times.
✓ Branch 2 taken 319492 times.
|
358388 | if (my_seq_no - atomic_read32(&flushed_) == flush_threshold_) { |
97 | 36539 | MutexLockGuard m(&sig_flush_mutex_); | |
98 | 36541 | int err_code __attribute__((unused)) = pthread_cond_signal(&sig_flush_); | |
99 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 36541 times.
|
36541 | assert(err_code == 0 && "Could not signal flush thread"); |
100 | 36541 | } | |
101 | |||
102 | 355926 | return my_seq_no; | |
103 | } | ||
104 | |||
105 | |||
106 | 1453 | void Tracer::Flush() { | |
107 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1453 times.
|
1453 | if (!active_) return; |
108 | |||
109 |
3/6✓ Branch 2 taken 1442 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 1468 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 1468 times.
✗ Branch 9 not taken.
|
1453 | int32_t save_seq_no = DoTrace(kEventFlush, PathString("Tracer", 6), |
110 | "flushed ring buffer"); | ||
111 |
2/2✓ Branch 1 taken 1265 times.
✓ Branch 2 taken 1450 times.
|
2744 | while (atomic_read32(&flushed_) <= save_seq_no) { |
112 | timespec timeout; | ||
113 | int retval; | ||
114 | |||
115 | 1265 | atomic_cas32(&flush_immediately_, 0, 1); | |
116 | { | ||
117 | 1267 | MutexLockGuard m(&sig_flush_mutex_); | |
118 | 1296 | retval = pthread_cond_signal(&sig_flush_); | |
119 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1296 times.
|
1296 | assert(retval == 0); |
120 | 1296 | } | |
121 | |||
122 | 1293 | GetTimespecRel(250, &timeout); | |
123 | 1293 | retval = pthread_mutex_lock(&sig_continue_trace_mutex_); | |
124 |
1/2✓ Branch 1 taken 1296 times.
✗ Branch 2 not taken.
|
1296 | retval |= pthread_cond_timedwait(&sig_continue_trace_, |
125 | &sig_continue_trace_mutex_, | ||
126 | &timeout); | ||
127 | 1296 | retval |= pthread_mutex_unlock(&sig_continue_trace_mutex_); | |
128 |
3/4✓ Branch 0 taken 1236 times.
✓ Branch 1 taken 44 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1236 times.
|
1280 | assert(retval == ETIMEDOUT || retval == 0); |
129 | } | ||
130 | } | ||
131 | |||
132 | |||
133 | 149813 | void Tracer::GetTimespecRel(const int64_t ms, timespec *ts) { | |
134 | timeval now; | ||
135 | 149813 | gettimeofday(&now, NULL); | |
136 | 149600 | int64_t nsecs = now.tv_usec * 1000 + (ms % 1000)*1000*1000; | |
137 | 149600 | int carry = 0; | |
138 |
2/2✓ Branch 0 taken 4168 times.
✓ Branch 1 taken 145432 times.
|
149600 | if (nsecs >= 1000*1000*1000) { |
139 | 4168 | carry = 1; | |
140 | 4168 | nsecs -= 1000*1000*1000; | |
141 | } | ||
142 | 149600 | ts->tv_sec = now.tv_sec + ms/1000 + carry; | |
143 | 149600 | ts->tv_nsec = nsecs; | |
144 | 149600 | } | |
145 | |||
146 | |||
147 | 14 | void *Tracer::MainFlush(void *data) { | |
148 | 14 | Tracer *tracer = reinterpret_cast<Tracer *>(data); | |
149 | int retval; | ||
150 | 14 | MutexLockGuard m(&tracer->sig_flush_mutex_); | |
151 |
1/2✓ Branch 2 taken 14 times.
✗ Branch 3 not taken.
|
14 | FILE *f = fopen(tracer->trace_file_.c_str(), "a"); |
152 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 14 times.
|
14 | assert(f != NULL && "Could not open trace file"); |
153 | struct timespec timeout; | ||
154 | |||
155 | do { | ||
156 |
2/2✓ Branch 1 taken 124535 times.
✓ Branch 2 taken 394 times.
|
249872 | while ((atomic_read32(&tracer->terminate_flush_thread_) == 0) && |
157 |
4/4✓ Branch 0 taken 124929 times.
✓ Branch 1 taken 14 times.
✓ Branch 3 taken 30598 times.
✓ Branch 4 taken 94345 times.
|
249872 | (atomic_read32(&tracer->flush_immediately_) == 0) && |
158 | 124535 | (atomic_read32(&tracer->seq_no_) - | |
159 | 124535 | atomic_read32(&tracer->flushed_) | |
160 |
2/2✓ Branch 0 taken 30598 times.
✓ Branch 1 taken 93937 times.
|
124535 | <= tracer->flush_threshold_)) |
161 | { | ||
162 | 30598 | tracer->GetTimespecRel(2000, &timeout); | |
163 |
1/2✓ Branch 1 taken 30598 times.
✗ Branch 2 not taken.
|
30598 | retval = pthread_cond_timedwait(&tracer->sig_flush_, |
164 | &tracer->sig_flush_mutex_, | ||
165 | &timeout); | ||
166 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 30598 times.
|
30598 | assert(retval != EINVAL); |
167 | } | ||
168 | |||
169 | 94345 | int base = atomic_read32(&tracer->flushed_) % tracer->buffer_size_; | |
170 | 94345 | int pos, i = 0; | |
171 |
6/6✓ Branch 0 taken 412083 times.
✓ Branch 1 taken 54891 times.
✓ Branch 2 taken 372629 times.
✓ Branch 3 taken 39454 times.
✓ Branch 4 taken 372629 times.
✓ Branch 5 taken 94345 times.
|
879057 | while ((i <= tracer->flush_threshold_) && |
172 | 412083 | (atomic_read32(&tracer->commit_buffer_[ | |
173 | 412083 | pos = ((base + i) % tracer->buffer_size_)]) == 1)) | |
174 | { | ||
175 | 372629 | string tmp; | |
176 |
1/2✓ Branch 1 taken 372629 times.
✗ Branch 2 not taken.
|
372629 | tmp = StringifyTimeval(tracer->ring_buffer_[pos].time_stamp); |
177 |
1/2✓ Branch 1 taken 372629 times.
✗ Branch 2 not taken.
|
372629 | retval = tracer->WriteCsvFile(f, tmp); |
178 |
1/2✓ Branch 1 taken 372629 times.
✗ Branch 2 not taken.
|
372629 | retval |= fputc(',', f) - ','; |
179 |
1/2✓ Branch 1 taken 372629 times.
✗ Branch 2 not taken.
|
372629 | tmp = StringifyInt(tracer->ring_buffer_[pos].code); |
180 |
1/2✓ Branch 1 taken 372629 times.
✗ Branch 2 not taken.
|
372629 | retval = tracer->WriteCsvFile(f, tmp); |
181 |
1/2✓ Branch 1 taken 372629 times.
✗ Branch 2 not taken.
|
372629 | retval |= fputc(',', f) - ','; |
182 |
1/2✓ Branch 1 taken 372629 times.
✗ Branch 2 not taken.
|
372629 | retval |= tracer->WriteCsvFile( |
183 |
1/2✓ Branch 1 taken 372629 times.
✗ Branch 2 not taken.
|
745258 | f, tracer->ring_buffer_[pos].path.ToString()); |
184 |
1/2✓ Branch 1 taken 372629 times.
✗ Branch 2 not taken.
|
372629 | retval |= fputc(',', f) - ','; |
185 |
1/2✓ Branch 1 taken 372629 times.
✗ Branch 2 not taken.
|
372629 | retval |= tracer->WriteCsvFile(f, tracer->ring_buffer_[pos].msg); |
186 |
2/4✓ Branch 1 taken 372629 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 372629 times.
✗ Branch 5 not taken.
|
372629 | retval |= (fputc(13, f) - 13) | (fputc(10, f) - 10); |
187 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 372629 times.
|
372629 | assert(retval == 0); |
188 | |||
189 | 372629 | atomic_dec32(&tracer->commit_buffer_[pos]); | |
190 | 372629 | ++i; | |
191 | 372629 | } | |
192 |
1/2✓ Branch 1 taken 94345 times.
✗ Branch 2 not taken.
|
94345 | retval = fflush(f); |
193 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 94345 times.
|
94345 | assert(retval == 0); |
194 | 94345 | atomic_xadd32(&tracer->flushed_, i); | |
195 | 94345 | atomic_cas32(&tracer->flush_immediately_, 1, 0); | |
196 | |||
197 | { | ||
198 | 94345 | MutexLockGuard l(&tracer->sig_continue_trace_mutex_); | |
199 | 94345 | retval = pthread_cond_broadcast(&tracer->sig_continue_trace_); | |
200 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 94345 times.
|
94345 | assert(retval == 0); |
201 | 94345 | } | |
202 |
6/6✓ Branch 1 taken 17 times.
✓ Branch 2 taken 94328 times.
✓ Branch 3 taken 3 times.
✓ Branch 4 taken 14 times.
✓ Branch 5 taken 94331 times.
✓ Branch 6 taken 14 times.
|
94362 | } while ((atomic_read32(&tracer->terminate_flush_thread_) == 0) || |
203 | 17 | (atomic_read32(&tracer->flushed_) < | |
204 | 17 | atomic_read32(&tracer->seq_no_))); | |
205 | |||
206 |
1/2✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
|
14 | retval = fclose(f); |
207 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 14 times.
|
14 | assert(retval == 0); |
208 | 14 | return NULL; | |
209 | 14 | } | |
210 | |||
211 | |||
212 | 15 | void Tracer::Spawn() { | |
213 |
2/2✓ Branch 0 taken 14 times.
✓ Branch 1 taken 1 times.
|
15 | if (active_) { |
214 | 14 | int retval = pthread_create(&thread_flush_, NULL, MainFlush, this); | |
215 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 14 times.
|
14 | assert(retval == 0); |
216 | |||
217 | 14 | spawned_ = true; | |
218 |
3/6✓ Branch 2 taken 14 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 14 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 14 times.
✗ Branch 9 not taken.
|
14 | DoTrace(kEventStart, PathString("Tracer", 6), "Trace buffer created"); |
219 | } | ||
220 | 15 | } | |
221 | |||
222 | |||
223 | 38 | Tracer::Tracer() | |
224 | 38 | : active_(false) | |
225 | 38 | , spawned_(false) | |
226 | 38 | , buffer_size_(0) | |
227 | 38 | , flush_threshold_(0) | |
228 | 38 | , ring_buffer_(NULL) | |
229 | 38 | , commit_buffer_(NULL) | |
230 | { | ||
231 | 38 | memset(&thread_flush_, 0, sizeof(thread_flush_)); | |
232 | 38 | atomic_init32(&seq_no_); | |
233 | 38 | atomic_init32(&flushed_); | |
234 | 38 | atomic_init32(&terminate_flush_thread_); | |
235 | 38 | atomic_init32(&flush_immediately_); | |
236 | 38 | } | |
237 | |||
238 | |||
239 | 52 | Tracer::~Tracer() { | |
240 |
2/2✓ Branch 0 taken 24 times.
✓ Branch 1 taken 14 times.
|
38 | if (!active_) |
241 | 24 | return; | |
242 | int retval; | ||
243 | |||
244 |
1/2✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
|
14 | if (spawned_) { |
245 | 14 | DoTrace(kEventStop, PathString("Tracer", 6), "Destroying trace buffer..."); | |
246 | |||
247 | // Trigger flushing and wait for it | ||
248 | 14 | atomic_inc32(&terminate_flush_thread_); | |
249 | { | ||
250 | 14 | MutexLockGuard m(&sig_flush_mutex_); | |
251 | 14 | retval = pthread_cond_signal(&sig_flush_); | |
252 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 14 times.
|
14 | assert(retval == 0); |
253 | 14 | } | |
254 | 14 | retval = pthread_join(thread_flush_, NULL); | |
255 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 14 times.
|
14 | assert(retval == 0); |
256 | } | ||
257 | |||
258 | 14 | retval = pthread_cond_destroy(&sig_continue_trace_); | |
259 | 14 | retval |= pthread_mutex_destroy(&sig_continue_trace_mutex_); | |
260 | 14 | retval |= pthread_cond_destroy(&sig_flush_); | |
261 | 14 | retval |= pthread_mutex_destroy(&sig_flush_mutex_); | |
262 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 14 times.
|
14 | assert(retval == 0); |
263 | |||
264 |
3/4✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4194 times.
✓ Branch 3 taken 14 times.
|
4208 | delete[] ring_buffer_; |
265 |
1/2✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
|
14 | delete[] commit_buffer_; |
266 |
2/2✓ Branch 1 taken 14 times.
✓ Branch 2 taken 24 times.
|
38 | } |
267 | |||
268 | |||
269 | 1490516 | int Tracer::WriteCsvFile(FILE *fp, const string &field) { | |
270 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1490516 times.
|
1490516 | if (fp == NULL) |
271 | ✗ | return 0; | |
272 | |||
273 | int retval; | ||
274 | |||
275 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 1490516 times.
|
1490516 | if ((retval = fputc('"', fp)) != '"') |
276 | ✗ | return retval; | |
277 | |||
278 |
2/2✓ Branch 1 taken 21489488 times.
✓ Branch 2 taken 1490516 times.
|
22980004 | for (unsigned i = 0, l = field.length(); i < l; ++i) { |
279 |
2/2✓ Branch 1 taken 261000 times.
✓ Branch 2 taken 21228488 times.
|
21489488 | if (field[i] == '"') { |
280 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 261000 times.
|
261000 | if ((retval = fputc('"', fp)) != '"') |
281 | ✗ | return retval; | |
282 | } | ||
283 |
1/2✗ Branch 3 not taken.
✓ Branch 4 taken 21489488 times.
|
21489488 | if ((retval = fputc(field[i], fp)) != field[i]) |
284 | ✗ | return retval; | |
285 | } | ||
286 | |||
287 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 1490516 times.
|
1490516 | if ((retval = fputc('"', fp)) != '"') |
288 | ✗ | return retval; | |
289 | |||
290 | 1490516 | return 0; | |
291 | } | ||
292 |