traceobj.c 7.71 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/*
 * Copyright (C) 2013 Philippe Gerum <rpm@xenomai.org>.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.

 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA.
 */
18
19
#include <stdio.h>
#include <stdlib.h>
20
#include "boilerplate/lock.h"
21
22
23
#include "copperplate/traceobj.h"
#include "copperplate/threadobj.h"
#include "copperplate/heapobj.h"
24
#include "xenomai/init.h"
25
#include "internal.h"
26
27
28
29
30
31
32
33
34
35
36
37
#ifdef CONFIG_XENO_VALGRIND_API
#include <valgrind/valgrind.h>
static inline int valgrind_detected(void)
{
	return RUNNING_ON_VALGRIND;
}
#else
static inline int valgrind_detected(void)
{
	return 0;
}
#endif
38
39
40
41
42
43
44

struct tracemark {
	const char *file;
	int line;
	int mark;
};

45
int traceobj_init(struct traceobj *trobj, const char *label, int nr_marks)
46
47
48
{
	pthread_mutexattr_t mattr;
	pthread_condattr_t cattr;
49
	int ret;
50

51
52
53
54
	pthread_mutexattr_init(&mattr);
	pthread_mutexattr_settype(&mattr, mutex_type_attribute);
	pthread_mutexattr_setprotocol(&mattr, PTHREAD_PRIO_INHERIT);
	pthread_mutexattr_setpshared(&mattr, PTHREAD_PROCESS_PRIVATE);
55
	ret = __bt(-__RT(pthread_mutex_init(&trobj->lock, &mattr)));
56
	pthread_mutexattr_destroy(&mattr);
57
58
59
	if (ret)
		return ret;

60
61
	pthread_condattr_init(&cattr);
	pthread_condattr_setpshared(&cattr, PTHREAD_PROCESS_PRIVATE);
62
	ret = __bt(-__RT(pthread_cond_init(&trobj->join, &cattr)));
63
	pthread_condattr_destroy(&cattr);
64
65
66
67
68
	if (ret) {
		__RT(pthread_mutex_destroy(&trobj->lock));
		return ret;
	}

69
70
71
72
73
74
	/*
	 * We make sure not to unblock from threadobj_join() until at
	 * least one thread has called trace_enter() for this trace
	 * object.
	 */
	trobj->nr_threads = -1;
75
76
77
78
79
80

	trobj->label = label;
	trobj->nr_marks = nr_marks;
	trobj->cur_mark = 0;

	if (nr_marks > 0) {
81
		trobj->marks = __STD(malloc(sizeof(struct tracemark) * nr_marks));
82
83
84
		if (trobj->marks == NULL)
			panic("cannot allocate mark table for tracing");
	}
85
86

	return 0;
87
88
89
90
91
92
93
94
95
96
97
}

static void compare_marks(struct traceobj *trobj, int tseq[], int nr_seq) /* lock held */
{
	int mark;

	for (mark = 0; mark < trobj->cur_mark || mark < nr_seq; mark++) {
		if (mark >= trobj->cur_mark) {
			fprintf(stderr, " <missing mark> |  [%d] expected\n",
				tseq[mark]);
		} else if (mark < nr_seq)
98
99
100
101
102
			__RT(fprintf(stderr, "at %s:%d  |  [%d] should be [%d]\n",
				     trobj->marks[mark].file,
				     trobj->marks[mark].line,
				     trobj->marks[mark].mark,
				     tseq[mark]));
103
		else
104
105
106
107
			__RT(fprintf(stderr, "at %s:%d  |  unexpected [%d]\n",
				     trobj->marks[mark].file,
				     trobj->marks[mark].line,
				     trobj->marks[mark].mark));
108
109
110
111
112
113
114
	}

	fflush(stderr);
}

void traceobj_verify(struct traceobj *trobj, int tseq[], int nr_seq)
{
115
	int end_mark, mark, state;
116
117
118
	struct service svc;

	CANCEL_DEFER(svc);
119

120
121
	read_lock_safe(&trobj->lock, state);

122
123
124
125
	if (nr_seq > trobj->nr_marks)
		goto fail;

	end_mark = trobj->cur_mark;
126
	if (end_mark == 0) {
127
		read_unlock_safe(&trobj->lock, state);
128
		panic("no mark defined");
129
	}
130
131
132
133
134
135
136
137

	if (end_mark != nr_seq)
		goto fail;

	for (mark = 0; mark < end_mark; mark++) {
		if (trobj->marks[mark].mark != tseq[mark])
			goto fail;
	}
138
out:
139
	read_unlock_safe(&trobj->lock, state);
140
141
142

	CANCEL_RESTORE(svc);

143
144
145
	return;

fail:
146
147
148
149
	if (valgrind_detected()) {
		warning("valgrind detected: ignoring sequence mismatch");
		goto out;
	}
150

151
152
	warning("mismatching execution sequence detected");
	compare_marks(trobj, tseq, nr_seq);
153
	read_unlock_safe(&trobj->lock, state);
154
155
156

	CANCEL_RESTORE(svc);

157
158
159
160
161
162
163
164
#ifdef CONFIG_XENO_MERCURY
	/*
	 * The Mercury core does not force any affinity, which may
	 * lead to wrong results with some unit tests checking strict
	 * ordering of operations. Tell the user about this. Normally,
	 * such unit tests on Mercury should be pinned on a single CPU
	 * using --cpu-affinity.
	 */
165
	if (CPU_COUNT(&__base_setup_data.cpu_affinity) == 0)
166
		warning("NOTE: --cpu-affinity option was not given - this might explain?");
167
168
169
170
171
172
173
#endif
#ifndef CONFIG_XENO_ASYNC_CANCEL
	/*
	 * Lack of async cancellation support might also explain why
	 * some tests have failed.
	 */
	warning("NOTE: --disable-async-cancel option was given - this might explain?");
174
#endif
175
176
177
178
179
	exit(5);
}

void traceobj_destroy(struct traceobj *trobj)
{
180
	__STD(free(trobj->marks));
181
	__RT(pthread_mutex_destroy(&trobj->lock));
182
183
184
185
186
187
188
}

static void dump_marks(struct traceobj *trobj) /* lock held */
{
	int mark;

	for (mark = 0; mark < trobj->cur_mark; mark++)
189
190
191
192
		fprintf(stderr, "[%d] at %s:%d\n",
			trobj->marks[mark].mark,
			trobj->marks[mark].file,
			trobj->marks[mark].line);
193
194
195
196

	fflush(stderr);
}

197
static void dump_marks_on_error(struct traceobj *trobj)
198
{
199
200
201
202
	struct service svc;

	CANCEL_DEFER(svc);

203
204
	push_cleanup_lock(&trobj->lock);
	read_lock(&trobj->lock);
205
	dump_marks(trobj);
206
207
208
	read_unlock(&trobj->lock);
	pop_cleanup_lock(&trobj->lock);

209
	CANCEL_RESTORE(svc);
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
}

void __traceobj_assert_failed(struct traceobj *trobj,
			      const char *file, int line, const char *cond)
{
	dump_marks_on_error(trobj);
	panic("trace assertion failed:\n              %s:%d => \"%s\"", file, line, cond);
}

void __traceobj_check_abort(struct traceobj *trobj,
			    const char *file, int line,
			    int received, int expected)
{
	dump_marks_on_error(trobj);
	panic("wrong return status:\n              %s:%d => %s (want %s)", file, line,
225
	      symerror(received), symerror(expected));
226
}
227

228
229
230
231
232
233
void __traceobj_check_warn(struct traceobj *trobj,
			   const char *file, int line,
			   int received, int expected)
{
	dump_marks_on_error(trobj);
	warning("wrong return status:\n              %s:%d => %s (want %s)", file, line,
234
		symerror(received), symerror(expected));
235
236
237
238
239
240
}

void __traceobj_mark(struct traceobj *trobj,
		     const char *file, int line, int mark)
{
	struct tracemark *tmk;
241
	struct service svc;
242
243
	int cur_mark;

244
245
	CANCEL_DEFER(svc);

246
	pthread_testcancel();
247
248
	push_cleanup_lock(&trobj->lock);
	write_lock(&trobj->lock);
249

250
	cur_mark = trobj->cur_mark;
251
252
	if (cur_mark >= trobj->nr_marks) {
		dump_marks(trobj);
253
		panic("too many marks: [%d] at %s:%d", mark, file, line);
254
	}
255
256
257
258
259

	tmk = trobj->marks + cur_mark;
	tmk->file = file;
	tmk->line = line;
	tmk->mark = mark;
260
	trobj->cur_mark++;
261

262
263
	write_unlock(&trobj->lock);
	pop_cleanup_lock(&trobj->lock);
264
265

	CANCEL_RESTORE(svc);
266
267
268
269
270
}

void traceobj_enter(struct traceobj *trobj)
{
	struct threadobj *current = threadobj_current();
271
	struct service svc;
272

273
	if (current)
274
275
		current->tracer = trobj;

276
277
278
279
	CANCEL_DEFER(svc);

	write_lock_nocancel(&trobj->lock);

280
281
	if (++trobj->nr_threads == 0)
		trobj->nr_threads = 1;
282

283
	write_unlock(&trobj->lock);
284
285

	CANCEL_RESTORE(svc);
286
287
288
289
290
}

/* May be directly called from finalizer. */
void traceobj_unwind(struct traceobj *trobj)
{
291
	struct service svc;
292
293
	int state;

294
295
	CANCEL_DEFER(svc);

296
	write_lock_safe(&trobj->lock, state);
297
298

	if (--trobj->nr_threads <= 0)
299
		threadobj_cond_signal(&trobj->join);
300

301
	write_unlock_safe(&trobj->lock, state);
302
303

	CANCEL_RESTORE(svc);
304
305
306
307
308
309
}

void traceobj_exit(struct traceobj *trobj)
{
	struct threadobj *current = threadobj_current();

310
	if (current)
311
312
313
314
315
316
317
		current->tracer = NULL;

	traceobj_unwind(trobj);
}

void traceobj_join(struct traceobj *trobj)
{
318
319
320
321
	struct service svc;

	CANCEL_DEFER(svc);

322
323
	push_cleanup_lock(&trobj->lock);
	read_lock(&trobj->lock);
324

325
	while (trobj->nr_threads < 0 || trobj->nr_threads > 0)
326
		threadobj_cond_wait(&trobj->join, &trobj->lock);
327

328
329
	read_unlock(&trobj->lock);
	pop_cleanup_lock(&trobj->lock);
330
331

	CANCEL_RESTORE(svc);
332
}