1/*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2018, Matthew Macy
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 *
27 */
28
29#include <sys/types.h>
30#include <sys/sysctl.h>
31#include <assert.h>
32#include <err.h>
33#include <errno.h>
34#include <limits.h>
35#include <stddef.h>
36#include <stdio.h>
37#include <stdlib.h>
38#include <string.h>
39#include <string>
40#include <sysexits.h>
41
42#include <pmc.h>
43#include <pmcformat.h>
44#include <pmclog.h>
45
46using std::string;
47
48static const char *typenames[] = {
49	"",
50	"{\"type\": \"closelog\"}\n",
51	"{\"type\": \"dropnotify\"}\n",
52	"{\"type\": \"initialize\"",
53	"",
54	"{\"type\": \"pmcallocate\"",
55	"{\"type\": \"pmcattach\"",
56	"{\"type\": \"pmcdetach\"",
57	"{\"type\": \"proccsw\"",
58	"{\"type\": \"procexec\"",
59	"{\"type\": \"procexit\"",
60	"{\"type\": \"procfork\"",
61	"{\"type\": \"sysexit\"",
62	"{\"type\": \"userdata\"",
63	"{\"type\": \"map_in\"",
64	"{\"type\": \"map_out\"",
65	"{\"type\": \"callchain\"",
66	"{\"type\": \"pmcallocatedyn\"",
67	"{\"type\": \"thr_create\"",
68	"{\"type\": \"thr_exit\"",
69	"{\"type\": \"proc_create\"",
70};
71
72static string
73startentry(struct pmclog_ev *ev)
74{
75	char eventbuf[128];
76
77	snprintf(eventbuf, sizeof(eventbuf), "%s, \"tsc\": \"%jd\"",
78	    typenames[ev->pl_type], (uintmax_t)ev->pl_ts.tv_sec);
79	return (string(eventbuf));
80}
81
82static string
83initialize_to_json(struct pmclog_ev *ev)
84{
85	char eventbuf[256];
86	string startent;
87
88	startent = startentry(ev);
89	snprintf(eventbuf, sizeof(eventbuf),
90	    "%s, \"version\": \"0x%08x\", \"arch\": \"0x%08x\", \"cpuid\": \"%s\", "
91		"\"tsc_freq\": \"%jd\", \"sec\": \"%jd\", \"nsec\": \"%jd\"}\n",
92		startent.c_str(), ev->pl_u.pl_i.pl_version, ev->pl_u.pl_i.pl_arch,
93		ev->pl_u.pl_i.pl_cpuid, (uintmax_t)ev->pl_u.pl_i.pl_tsc_freq,
94		(uintmax_t)ev->pl_u.pl_i.pl_ts.tv_sec, (uintmax_t)ev->pl_u.pl_i.pl_ts.tv_nsec);
95	return string(eventbuf);
96}
97
98static string
99pmcallocate_to_json(struct pmclog_ev *ev)
100{
101	char eventbuf[256];
102	string startent;
103
104	startent = startentry(ev);
105	snprintf(eventbuf, sizeof(eventbuf),
106	    "%s, \"pmcid\": \"0x%08x\", \"event\": \"0x%08x\", \"flags\": \"0x%08x\", "
107	    "\"rate\": \"%jd\"}\n",
108		startent.c_str(), ev->pl_u.pl_a.pl_pmcid, ev->pl_u.pl_a.pl_event,
109	    ev->pl_u.pl_a.pl_flags, (intmax_t)ev->pl_u.pl_a.pl_rate);
110	return string(eventbuf);
111}
112
113static string
114pmcattach_to_json(struct pmclog_ev *ev)
115{
116	char eventbuf[2048];
117	string startent;
118
119	startent = startentry(ev);
120	snprintf(eventbuf, sizeof(eventbuf),
121	    "%s, \"pmcid\": \"0x%08x\", \"pid\": \"%d\", \"pathname\": \"%s\"}\n",
122		startent.c_str(), ev->pl_u.pl_t.pl_pmcid, ev->pl_u.pl_t.pl_pid,
123	    ev->pl_u.pl_t.pl_pathname);
124	return string(eventbuf);
125}
126
127static string
128pmcdetach_to_json(struct pmclog_ev *ev)
129{
130	char eventbuf[128];
131	string startent;
132
133	startent = startentry(ev);
134	snprintf(eventbuf, sizeof(eventbuf),
135		"%s, \"pmcid\": \"0x%08x\", \"pid\": \"%d\"}\n",
136			 startent.c_str(), ev->pl_u.pl_d.pl_pmcid, ev->pl_u.pl_d.pl_pid);
137	return string(eventbuf);
138}
139
140
141static string
142proccsw_to_json(struct pmclog_ev *ev)
143{
144	char eventbuf[128];
145	string startent;
146
147	startent = startentry(ev);
148	snprintf(eventbuf, sizeof(eventbuf), "%s, \"pmcid\": \"0x%08x\", \"pid\": \"%d\" "
149	    "\"tid\": \"%d\", \"value\": \"0x%016jx\"}\n",
150		startent.c_str(), ev->pl_u.pl_c.pl_pmcid, ev->pl_u.pl_c.pl_pid,
151	    ev->pl_u.pl_c.pl_tid, (uintmax_t)ev->pl_u.pl_c.pl_value);
152	return string(eventbuf);
153}
154
155static string
156procexec_to_json(struct pmclog_ev *ev)
157{
158	char eventbuf[2048];
159	string startent;
160
161	startent = startentry(ev);
162	snprintf(eventbuf, sizeof(eventbuf),
163		"%s, \"pmcid\": \"0x%08x\", \"pid\": \"%d\", "
164	    "\"base\": \"0x%016jx\", \"dyn\": \"0x%016jx\", "
165	    "\"pathname\": \"%s\"}\n",
166		startent.c_str(), ev->pl_u.pl_x.pl_pmcid, ev->pl_u.pl_x.pl_pid,
167		(uintmax_t)ev->pl_u.pl_x.pl_baseaddr,
168		(uintmax_t)ev->pl_u.pl_x.pl_dynaddr,
169		ev->pl_u.pl_x.pl_pathname);
170	return string(eventbuf);
171}
172
173static string
174procexit_to_json(struct pmclog_ev *ev)
175{
176	char eventbuf[128];
177	string startent;
178
179	startent = startentry(ev);
180	snprintf(eventbuf, sizeof(eventbuf),
181		"%s, \"pmcid\": \"0x%08x\", \"pid\": \"%d\", "
182	    "\"value\": \"0x%016jx\"}\n",
183		startent.c_str(), ev->pl_u.pl_e.pl_pmcid, ev->pl_u.pl_e.pl_pid,
184	    (uintmax_t)ev->pl_u.pl_e.pl_value);
185	return string(eventbuf);
186}
187
188static string
189procfork_to_json(struct pmclog_ev *ev)
190{
191	char eventbuf[128];
192	string startent;
193
194	startent = startentry(ev);
195	snprintf(eventbuf, sizeof(eventbuf),
196		"%s, \"oldpid\": \"%d\", \"newpid\": \"%d\"}\n",
197		startent.c_str(), ev->pl_u.pl_f.pl_oldpid, ev->pl_u.pl_f.pl_newpid);
198	return string(eventbuf);
199}
200
201static string
202sysexit_to_json(struct pmclog_ev *ev)
203{
204	char eventbuf[128];
205	string startent;
206
207	startent = startentry(ev);
208	snprintf(eventbuf, sizeof(eventbuf), "%s, \"pid\": \"%d\"}\n",
209		startent.c_str(), ev->pl_u.pl_se.pl_pid);
210	return string(eventbuf);
211}
212
213static string
214userdata_to_json(struct pmclog_ev *ev)
215{
216	char eventbuf[128];
217	string startent;
218
219	startent = startentry(ev);
220	snprintf(eventbuf, sizeof(eventbuf), "%s, \"userdata\": \"0x%08x\"}\n",
221	    startent.c_str(), ev->pl_u.pl_u.pl_userdata);
222	return string(eventbuf);
223}
224
225static string
226map_in_to_json(struct pmclog_ev *ev)
227{
228	char eventbuf[2048];
229	string startent;
230
231	startent = startentry(ev);
232	snprintf(eventbuf, sizeof(eventbuf), "%s, \"pid\": \"%d\", "
233	    "\"start\": \"0x%016jx\", \"pathname\": \"%s\"}\n",
234	    startent.c_str(), ev->pl_u.pl_mi.pl_pid,
235	    (uintmax_t)ev->pl_u.pl_mi.pl_start, ev->pl_u.pl_mi.pl_pathname);
236	return string(eventbuf);
237}
238
239static string
240map_out_to_json(struct pmclog_ev *ev)
241{
242	char eventbuf[256];
243	string startent;
244
245	startent = startentry(ev);
246	snprintf(eventbuf, sizeof(eventbuf), "%s, \"pid\": \"%d\", "
247	    "\"start\": \"0x%016jx\", \"end\": \"0x%016jx\"}\n",
248	    startent.c_str(), ev->pl_u.pl_mi.pl_pid,
249	    (uintmax_t)ev->pl_u.pl_mi.pl_start,
250	    (uintmax_t)ev->pl_u.pl_mo.pl_end);
251	return string(eventbuf);
252}
253
254static string
255callchain_to_json(struct pmclog_ev *ev)
256{
257	char eventbuf[1024];
258	string result;
259	uint32_t i;
260	string startent;
261
262	startent = startentry(ev);
263	snprintf(eventbuf, sizeof(eventbuf),
264	    "%s, \"pmcid\": \"0x%08x\", \"pid\": \"%d\", \"tid\": \"%d\", "
265	    "\"cpuflags\": \"0x%08x\", \"cpuflags2\": \"0x%08x\", \"pc\": [ ",
266		startent.c_str(), ev->pl_u.pl_cc.pl_pmcid, ev->pl_u.pl_cc.pl_pid,
267	    ev->pl_u.pl_cc.pl_tid, ev->pl_u.pl_cc.pl_cpuflags, ev->pl_u.pl_cc.pl_cpuflags2);
268	result = string(eventbuf);
269	for (i = 0; i < ev->pl_u.pl_cc.pl_npc - 1; i++) {
270		snprintf(eventbuf, sizeof(eventbuf), "\"0x%016jx\", ", (uintmax_t)ev->pl_u.pl_cc.pl_pc[i]);
271		result += string(eventbuf);
272	}
273	snprintf(eventbuf, sizeof(eventbuf), "\"0x%016jx\"]}\n", (uintmax_t)ev->pl_u.pl_cc.pl_pc[i]);
274	result += string(eventbuf);
275	return (result);
276}
277
278static string
279pmcallocatedyn_to_json(struct pmclog_ev *ev)
280{
281	char eventbuf[2048];
282	string startent;
283
284	startent = startentry(ev);
285	snprintf(eventbuf, sizeof(eventbuf),
286	    "%s, \"pmcid\": \"0x%08x\", \"event\": \"%d\", \"flags\": \"0x%08x\", \"evname\": \"%s\"}\n",
287	    startent.c_str(), ev->pl_u.pl_ad.pl_pmcid, ev->pl_u.pl_ad.pl_event,
288	    ev->pl_u.pl_ad.pl_flags, ev->pl_u.pl_ad.pl_evname);
289	return string(eventbuf);
290}
291
292static string
293proccreate_to_json(struct pmclog_ev *ev)
294{
295	char eventbuf[2048];
296	string startent;
297
298	startent = startentry(ev);
299	snprintf(eventbuf, sizeof(eventbuf),
300	    "%s, \"pid\": \"%d\", \"flags\": \"0x%08x\", \"pcomm\": \"%s\"}\n",
301	    startent.c_str(), ev->pl_u.pl_pc.pl_pid,
302	    ev->pl_u.pl_pc.pl_flags, ev->pl_u.pl_pc.pl_pcomm);
303	return string(eventbuf);
304}
305
306static string
307threadcreate_to_json(struct pmclog_ev *ev)
308{
309	char eventbuf[2048];
310	string startent;
311
312	startent = startentry(ev);
313	snprintf(eventbuf, sizeof(eventbuf),
314	    "%s, \"tid\": \"%d\", \"pid\": \"%d\", \"flags\": \"0x%08x\", \"tdname\": \"%s\"}\n",
315	    startent.c_str(), ev->pl_u.pl_tc.pl_tid, ev->pl_u.pl_tc.pl_pid,
316	    ev->pl_u.pl_tc.pl_flags, ev->pl_u.pl_tc.pl_tdname);
317	return string(eventbuf);
318}
319
320static string
321threadexit_to_json(struct pmclog_ev *ev)
322{
323	char eventbuf[256];
324	string startent;
325
326	startent = startentry(ev);
327	snprintf(eventbuf, sizeof(eventbuf), "%s, \"tid\": \"%d\"}\n",
328	    startent.c_str(), ev->pl_u.pl_te.pl_tid);
329	return string(eventbuf);
330}
331
332static string
333stub_to_json(struct pmclog_ev *ev)
334{
335	string startent;
336
337	startent = startentry(ev);
338	startent += string("}\n");
339	return startent;
340}
341
342typedef string (*jconv) (struct pmclog_ev*);
343
344static jconv jsonconvert[] = {
345	NULL,
346	stub_to_json,
347	stub_to_json,
348	initialize_to_json,
349	NULL,
350	pmcallocate_to_json,
351	pmcattach_to_json,
352	pmcdetach_to_json,
353	proccsw_to_json,
354	procexec_to_json,
355	procexit_to_json,
356	procfork_to_json,
357	sysexit_to_json,
358	userdata_to_json,
359	map_in_to_json,
360	map_out_to_json,
361	callchain_to_json,
362	pmcallocatedyn_to_json,
363	threadcreate_to_json,
364	threadexit_to_json,
365	proccreate_to_json,
366};
367
368string
369event_to_json(struct pmclog_ev *ev){
370
371	switch (ev->pl_type) {
372	case PMCLOG_TYPE_DROPNOTIFY:
373	case PMCLOG_TYPE_CLOSELOG:
374	case PMCLOG_TYPE_INITIALIZE:
375	case PMCLOG_TYPE_PMCALLOCATE:
376	case PMCLOG_TYPE_PMCATTACH:
377	case PMCLOG_TYPE_PMCDETACH:
378	case PMCLOG_TYPE_PROCCSW:
379	case PMCLOG_TYPE_PROCEXEC:
380	case PMCLOG_TYPE_PROCEXIT:
381	case PMCLOG_TYPE_PROCFORK:
382	case PMCLOG_TYPE_SYSEXIT:
383	case PMCLOG_TYPE_USERDATA:
384	case PMCLOG_TYPE_MAP_IN:
385	case PMCLOG_TYPE_MAP_OUT:
386	case PMCLOG_TYPE_CALLCHAIN:
387	case PMCLOG_TYPE_PMCALLOCATEDYN:
388	case PMCLOG_TYPE_THR_CREATE:
389	case PMCLOG_TYPE_THR_EXIT:
390	case PMCLOG_TYPE_PROC_CREATE:
391		return jsonconvert[ev->pl_type](ev);
392	default:
393		errx(EX_USAGE, "ERROR: unrecognized event type: %d\n", ev->pl_type);
394	}
395}
396
397