1 /*******************************************************************************
2 * Copyright (c) 2013, 2015 Ericsson
4 * All rights reserved. This program and the accompanying materials are
5 * made available under the terms of the Eclipse Public License v1.0 which
6 * accompanies this distribution, and is available at
7 * http://www.eclipse.org/legal/epl-v10.html
10 * Matthew Khouzam - Initial API and implementation
11 * Marc-Andre Laperle - Move generation to traces folder
12 *******************************************************************************/
14 package org
.eclipse
.tracecompass
.ctf
.core
.tests
.shared
;
17 import java
.io
.FileNotFoundException
;
18 import java
.io
.FileOutputStream
;
19 import java
.io
.IOException
;
20 import java
.nio
.ByteBuffer
;
21 import java
.nio
.ByteOrder
;
22 import java
.nio
.channels
.FileChannel
;
23 import java
.nio
.file
.Path
;
24 import java
.nio
.file
.Paths
;
25 import java
.util
.Arrays
;
26 import java
.util
.List
;
27 import java
.util
.Random
;
29 import org
.eclipse
.tracecompass
.ctf
.core
.tests
.CtfCoreTestPlugin
;
32 * Generate a kernel trace
34 * @author Matthew Khouzam
36 public class LttngKernelTraceGenerator
{
38 private static final String metadata
= "/* CTF 1.8 */ \n" +
39 "typealias integer { size = 8; align = 8; signed = false; } := uint8_t;\n" +
40 "typealias integer { size = 16; align = 8; signed = false; } := uint16_t;\n" +
41 "typealias integer { size = 32; align = 8; signed = false; } := uint32_t;\n" +
42 "typealias integer { size = 64; align = 8; signed = false; } := uint64_t;\n" +
43 "typealias integer { size = 32; align = 8; signed = false; } := unsigned long;\n" +
44 "typealias integer { size = 5; align = 1; signed = false; } := uint5_t;\n" +
45 "typealias integer { size = 27; align = 1; signed = false; } := uint27_t;\n" +
50 " uuid = \"11111111-1111-1111-1111-111111111111\";\n" +
51 " byte_order = le;\n" +
52 " packet.header := struct {\n" +
53 " uint32_t magic;\n" +
54 " uint8_t uuid[16];\n" +
55 " uint32_t stream_id;\n" +
60 " hostname = \"synthetic-host\";\n" +
61 " domain = \"kernel\";\n" +
62 " sysname = \"FakeLinux\";\n" +
63 " kernel_release = \"1.0\";\n" +
64 " kernel_version = \"Fake Os Synthetic Trace\";\n" +
65 " tracer_name = \"lttng-modules\";\n" +
66 " tracer_major = 2;\n" +
67 " tracer_minor = 1;\n" +
68 " tracer_patchlevel = 0;\n" +
72 " name = monotonic;\n" +
73 " uuid = \"bbff68f0-c633-4ea1-92cd-bd11024ec4de\";\n" +
74 " description = \"Monotonic Clock\";\n" +
75 " freq = 1000000000; /* Frequency, in Hz */\n" +
76 " /* clock value offset from Epoch is: offset * (1/freq) */\n" +
77 " offset = 1368000272650993664;\n" +
80 "typealias integer {\n" +
81 " size = 27; align = 1; signed = false;\n" +
82 " map = clock.monotonic.value;\n" +
83 "} := uint27_clock_monotonic_t;\n" +
85 "typealias integer {\n" +
86 " size = 32; align = 8; signed = false;\n" +
87 " map = clock.monotonic.value;\n" +
88 "} := uint32_clock_monotonic_t;\n" +
90 "typealias integer {\n" +
91 " size = 64; align = 8; signed = false;\n" +
92 " map = clock.monotonic.value;\n" +
93 "} := uint64_clock_monotonic_t;\n" +
95 "struct packet_context {\n" +
96 " uint64_clock_monotonic_t timestamp_begin;\n" +
97 " uint64_clock_monotonic_t timestamp_end;\n" +
98 " uint64_t content_size;\n" +
99 " uint64_t packet_size;\n" +
100 " unsigned long events_discarded;\n" +
101 " uint32_t cpu_id;\n" +
104 "struct event_header_compact {\n" +
105 " enum : uint5_t { compact = 0 ... 30, extended = 31 } id;\n" +
106 " variant <id> {\n" +
108 " uint27_clock_monotonic_t timestamp;\n" +
112 " uint64_clock_monotonic_t timestamp;\n" +
117 "struct event_header_large {\n" +
118 " enum : uint16_t { compact = 0 ... 65534, extended = 65535 } id;\n" +
119 " variant <id> {\n" +
121 " uint32_clock_monotonic_t timestamp;\n" +
125 " uint64_clock_monotonic_t timestamp;\n" +
132 " event.header := struct event_header_compact;\n" +
133 " packet.context := struct packet_context;\n" +
137 " name = sched_switch;\n" +
139 " stream_id = 0;\n" +
140 " fields := struct {\n" +
141 " integer { size = 8; align = 8; signed = 1; encoding = UTF8; base = 10; } _prev_comm[16];\n" +
142 " integer { size = 32; align = 8; signed = 1; encoding = none; base = 10; } _prev_tid;\n" +
143 " integer { size = 32; align = 8; signed = 1; encoding = none; base = 10; } _prev_prio;\n" +
144 " integer { size = 32; align = 8; signed = 1; encoding = none; base = 10; } _prev_state;\n" +
145 " integer { size = 8; align = 8; signed = 1; encoding = UTF8; base = 10; } _next_comm[16];\n" +
146 " integer { size = 32; align = 8; signed = 1; encoding = none; base = 10; } _next_tid;\n" +
147 " integer { size = 32; align = 8; signed = 1; encoding = none; base = 10; } _next_prio;\n" +
152 private final List
<String
> fProcesses
;
153 private final long fDuration
;
154 private final long fNbEvents
;
155 private final int fNbChans
;
157 private static final String
[] sfProcesses
= {
170 private static final String TRACES_DIRECTORY
= "traces";
171 private static final String TRACE_NAME
= "synthetic-trace";
174 * Main, not always needed
179 public static void main(String
[] args
) {
180 // not using createTempFile as this is a directory
181 String path
= CtfCoreTestPlugin
.getTemporaryDirPath() + File
.separator
+ TRACE_NAME
;
182 generateLttngKernelTrace(new File(path
));
186 * Gets the name of the trace (top directory name)
188 * @return the name of the trace
190 public static String
getName() {
199 public static String
getPath() {
200 CtfCoreTestPlugin plugin
= CtfCoreTestPlugin
.getDefault();
201 if (plugin
== null) {
204 Path tracePath
= Paths
.get("..", "..", "ctf", "org.eclipse.tracecompass.ctf.core.tests", TRACES_DIRECTORY
, TRACE_NAME
);
205 tracePath
= tracePath
.toAbsolutePath();
206 File file
= tracePath
.toFile();
208 if (!file
.exists()) {
209 generateLttngKernelTrace(file
);
211 return file
.getAbsolutePath();
218 * the file to write the trace to
220 public static void generateLttngKernelTrace(File file
) {
222 LttngKernelTraceGenerator gt
= new LttngKernelTraceGenerator(2l * Integer
.MAX_VALUE
- 100, 500000, cpus
);
227 * Make a kernel trace
230 * the duration of the trace
232 * the number of events in a trace
234 * the number of channels in the trace
236 public LttngKernelTraceGenerator(long duration
, long events
, int nbChannels
) {
237 fProcesses
= Arrays
.asList(sfProcesses
);
238 fDuration
= duration
;
240 fNbChans
= nbChannels
;
244 * Write the trace to a file
247 * the file to write the trace to
249 public void writeTrace(File file
) {
251 if (!file
.exists()) {
258 // the ctf parser doesn't recurse, so we don't need to.
259 final File
[] listFiles
= file
.listFiles();
260 for (File child
: listFiles
) {
266 File metadataFile
= new File(file
.getPath() + File
.separator
+ "metadata");
267 File
[] streams
= new File
[fNbChans
];
268 FileChannel
[] channels
= new FileChannel
[fNbChans
];
271 for (int i
= 0; i
< fNbChans
; i
++) {
272 streams
[i
] = new File(file
.getPath() + File
.separator
+ "channel" + i
);
273 channels
[i
] = new FileOutputStream(streams
[i
]).getChannel();
275 } catch (FileNotFoundException e
) {
277 // determine the number of events per channel
278 long evPerChan
= fNbEvents
/ fNbChans
;
279 final int evPerPacket
= PacketWriter
.CONTENT_SIZE
/ EventWriter
.SIZE
;
280 long delta
= (int) (fDuration
/ evPerChan
);
282 Random rndLost
= new Random(1337);
283 for (int chan
= 0; chan
< fNbChans
; chan
++) {
284 int currentSpace
= 0;
285 ByteBuffer bb
= ByteBuffer
.allocate(65536);
286 bb
.order(ByteOrder
.LITTLE_ENDIAN
);
287 Random rnd
= new Random(1337);
288 int rnd0
= rnd
.nextInt(fProcesses
.size());
289 String prevComm
= fProcesses
.get(rnd0
);
290 int prevPID
= rnd0
+ chan
* fProcesses
.size();
297 int discardedTotal
= 0;
298 for (int eventNb
= 0; eventNb
< evPerChan
; eventNb
++) {
299 if (EventWriter
.SIZE
> currentSpace
) {
300 eventNb
+= discarded
;
302 long ts
= eventNb
* delta
+ delta
/ (fNbChans
+ 1) * chan
;
304 int pos
= rnd
.nextInt((int) (fProcesses
.size() * 1.5));
305 if (pos
>= fProcesses
.size()) {
308 while (pos
== prevPos
) {
309 pos
= rnd
.nextInt((int) (fProcesses
.size() * 1.5));
310 if (pos
>= fProcesses
.size()) {
314 String nextComm
= fProcesses
.get(pos
);
315 int nextPID
= pos
+ fProcesses
.size() * chan
;
320 if (EventWriter
.SIZE
> currentSpace
) {
322 for (int i
= 0; i
< currentSpace
; i
++) {
326 PacketWriter pw
= new PacketWriter(bb
);
329 int eventCount
= Math
.min(evPerPacket
, (int) evPerChan
- eventNb
);
330 discarded
= rndLost
.nextInt(10 * fNbChans
) == 0 ? rndLost
.nextInt(evPerPacket
) : 0;
331 discarded
= Math
.min(discarded
, (int) evPerChan
- eventNb
- eventCount
);
332 discardedTotal
+= discarded
;
333 long tsEnd
= (eventNb
+ eventCount
+ discarded
) * delta
;
334 pw
.writeNewHeader(tsBegin
, tsEnd
, chan
, eventCount
, discardedTotal
);
335 currentSpace
= PacketWriter
.CONTENT_SIZE
;
337 EventWriter ew
= new EventWriter(bb
);
338 int prev_state
= rnd
.nextInt(100);
339 if (prev_state
!= 0) {
342 final long shrunkenTimestamp
= ts
- offsetTime
;
343 final int tsMask
= (1 << 27) - 1;
344 if (shrunkenTimestamp
> ((1 << 27) + tsMask
)) {
345 /* allow only one compact timestamp overflow per packet */
346 throw new IllegalStateException("Invalid timestamp overflow:" + shrunkenTimestamp
);
348 final int clampedTs
= (int) (ts
& tsMask
);
349 int evSize
= ew
.writeEvent(clampedTs
, prevComm
, prevPID
, prevPrio
, prev_state
, nextComm
, nextPID
, nextPrio
);
350 currentSpace
-= evSize
;
354 if (bb
.position() > 63000) {
355 writeToDisk(channels
, chan
, bb
);
358 for (int i
= 0; i
< currentSpace
; i
++) {
361 writeToDisk(channels
, chan
, bb
);
363 channels
[chan
].close();
364 } catch (IOException e
) {
368 try (FileOutputStream fos
= new FileOutputStream(metadataFile
);) {
369 fos
.write(metadata
.getBytes());
370 } catch (IOException e
) {
374 private static void writeToDisk(FileChannel
[] channels
, int chan
, ByteBuffer bb
) {
377 channels
[chan
].write(bb
);
379 } catch (IOException e
) {
384 private class EventWriter
{
385 public static final int SIZE
=
394 private final ByteBuffer data
;
396 public EventWriter(ByteBuffer bb
) {
400 public int writeEvent(int ts
, String prev_comm
, int prev_tid
, int prev_prio
, int prev_state
, String next_comm
, int next_tid
, int next_prio
) {
401 byte[] bOut
= new byte[16];
402 byte[] bIn
= new byte[16];
403 byte[] temp
= prev_comm
.getBytes();
404 for (int i
= 0; i
< Math
.min(temp
.length
, 16); i
++) {
407 temp
= next_comm
.getBytes();
408 for (int i
= 0; i
< Math
.min(temp
.length
, 16); i
++) {
412 int timestamp
= ts
<< 5;
414 data
.putInt(timestamp
);
416 data
.putInt(prev_tid
);
417 data
.putInt(prev_prio
);
418 data
.putInt(prev_state
);
420 data
.putInt(next_tid
);
421 data
.putInt(next_prio
);
427 private class PacketWriter
{
428 private static final int SIZE
= 4096;
429 private static final int HEADER_SIZE
= 64;
430 private static final int CONTENT_SIZE
= SIZE
- HEADER_SIZE
;
432 private final ByteBuffer data
;
434 public PacketWriter(ByteBuffer bb
) {
438 public void writeNewHeader(long tsBegin
, long tsEnd
, int cpu
, int eventCount
, int discarded
) {
439 final int magicLE
= 0xC1FC1FC1;
441 0x11, 0x11, 0x11, 0x11,
442 0x11, 0x11, 0x11, 0x11,
443 0x11, 0x11, 0x11, 0x11,
444 0x11, 0x11, 0x11, 0x11 };
448 data
.putInt(magicLE
);
456 data
.putLong(tsBegin
);
462 data
.putLong((eventCount
* EventWriter
.SIZE
+ HEADER_SIZE
)* 8);
465 data
.putLong((SIZE
) * 8);
467 // events_discarded 4
468 data
.putInt(discarded
);