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
.FileVisitResult
;
24 import java
.nio
.file
.Files
;
25 import java
.nio
.file
.Path
;
26 import java
.nio
.file
.Paths
;
27 import java
.nio
.file
.SimpleFileVisitor
;
28 import java
.nio
.file
.attribute
.BasicFileAttributes
;
29 import java
.util
.Arrays
;
30 import java
.util
.List
;
31 import java
.util
.Random
;
33 import org
.eclipse
.tracecompass
.ctf
.core
.tests
.CtfCoreTestPlugin
;
36 * Generate a kernel trace
38 * @author Matthew Khouzam
40 public class LttngKernelTraceGenerator
{
42 private static final String metadata
= "/* CTF 1.8 */ \n" +
43 "typealias integer { size = 8; align = 8; signed = false; } := uint8_t;\n" +
44 "typealias integer { size = 16; align = 8; signed = false; } := uint16_t;\n" +
45 "typealias integer { size = 32; align = 8; signed = false; } := uint32_t;\n" +
46 "typealias integer { size = 64; align = 8; signed = false; } := uint64_t;\n" +
47 "typealias integer { size = 32; align = 8; signed = false; } := unsigned long;\n" +
48 "typealias integer { size = 5; align = 1; signed = false; } := uint5_t;\n" +
49 "typealias integer { size = 27; align = 1; signed = false; } := uint27_t;\n" +
54 " uuid = \"11111111-1111-1111-1111-111111111111\";\n" +
55 " byte_order = le;\n" +
56 " packet.header := struct {\n" +
57 " uint32_t magic;\n" +
58 " uint8_t uuid[16];\n" +
59 " uint32_t stream_id;\n" +
64 " hostname = \"synthetic-host\";\n" +
65 " domain = \"kernel\";\n" +
66 " sysname = \"FakeLinux\";\n" +
67 " kernel_release = \"1.0\";\n" +
68 " kernel_version = \"Fake Os Synthetic Trace\";\n" +
69 " tracer_name = \"lttng-modules\";\n" +
70 " tracer_major = 2;\n" +
71 " tracer_minor = 1;\n" +
72 " tracer_patchlevel = 0;\n" +
76 " name = monotonic;\n" +
77 " uuid = \"bbff68f0-c633-4ea1-92cd-bd11024ec4de\";\n" +
78 " description = \"Monotonic Clock\";\n" +
79 " freq = 1000000000; /* Frequency, in Hz */\n" +
80 " /* clock value offset from Epoch is: offset * (1/freq) */\n" +
81 " offset = 1368000272650993664;\n" +
84 "typealias integer {\n" +
85 " size = 27; align = 1; signed = false;\n" +
86 " map = clock.monotonic.value;\n" +
87 "} := uint27_clock_monotonic_t;\n" +
89 "typealias integer {\n" +
90 " size = 32; align = 8; signed = false;\n" +
91 " map = clock.monotonic.value;\n" +
92 "} := uint32_clock_monotonic_t;\n" +
94 "typealias integer {\n" +
95 " size = 64; align = 8; signed = false;\n" +
96 " map = clock.monotonic.value;\n" +
97 "} := uint64_clock_monotonic_t;\n" +
99 "struct packet_context {\n" +
100 " uint64_clock_monotonic_t timestamp_begin;\n" +
101 " uint64_clock_monotonic_t timestamp_end;\n" +
102 " uint64_t content_size;\n" +
103 " uint64_t packet_size;\n" +
104 " unsigned long events_discarded;\n" +
105 " uint32_t cpu_id;\n" +
108 "struct event_header_compact {\n" +
109 " enum : uint5_t { compact = 0 ... 30, extended = 31 } id;\n" +
110 " variant <id> {\n" +
112 " uint27_clock_monotonic_t timestamp;\n" +
116 " uint64_clock_monotonic_t timestamp;\n" +
121 "struct event_header_large {\n" +
122 " enum : uint16_t { compact = 0 ... 65534, extended = 65535 } id;\n" +
123 " variant <id> {\n" +
125 " uint32_clock_monotonic_t timestamp;\n" +
129 " uint64_clock_monotonic_t timestamp;\n" +
136 " event.header := struct event_header_compact;\n" +
137 " packet.context := struct packet_context;\n" +
141 " name = sched_switch;\n" +
143 " stream_id = 0;\n" +
144 " fields := struct {\n" +
145 " integer { size = 8; align = 8; signed = 1; encoding = UTF8; base = 10; } _prev_comm[16];\n" +
146 " integer { size = 32; align = 8; signed = 1; encoding = none; base = 10; } _prev_tid;\n" +
147 " integer { size = 32; align = 8; signed = 1; encoding = none; base = 10; } _prev_prio;\n" +
148 " integer { size = 32; align = 8; signed = 1; encoding = none; base = 10; } _prev_state;\n" +
149 " integer { size = 8; align = 8; signed = 1; encoding = UTF8; base = 10; } _next_comm[16];\n" +
150 " integer { size = 32; align = 8; signed = 1; encoding = none; base = 10; } _next_tid;\n" +
151 " integer { size = 32; align = 8; signed = 1; encoding = none; base = 10; } _next_prio;\n" +
156 private final List
<String
> fProcesses
;
157 private final long fDuration
;
158 private final long fNbEvents
;
159 private final int fNbChans
;
161 private static final String
[] sfProcesses
= {
174 private static final String TRACES_DIRECTORY
= "traces";
175 private static final String TRACE_NAME
= "synthetic-trace";
178 * Main, not always needed
183 public static void main(String
[] args
) {
184 // not using createTempFile as this is a directory
185 String path
= CtfCoreTestPlugin
.getTemporaryDirPath() + File
.separator
+ TRACE_NAME
;
186 generateLttngKernelTrace(new File(path
));
190 * Gets the name of the trace (top directory name)
192 * @return the name of the trace
194 public static String
getName() {
203 public static String
getPath() {
204 CtfCoreTestPlugin plugin
= CtfCoreTestPlugin
.getDefault();
205 if (plugin
== null) {
208 Path tracePath
= Paths
.get("..", "..", "ctf", "org.eclipse.tracecompass.ctf.core.tests", TRACES_DIRECTORY
, TRACE_NAME
);
209 tracePath
= tracePath
.toAbsolutePath();
210 File file
= tracePath
.toFile();
211 generateLttngKernelTrace(file
);
212 return file
.getAbsolutePath();
219 * the file to write the trace to
221 public static void generateLttngKernelTrace(File file
) {
223 LttngKernelTraceGenerator gt
= new LttngKernelTraceGenerator(2l * Integer
.MAX_VALUE
- 100, 500000, cpus
);
228 * Make a kernel trace
231 * the duration of the trace
233 * the number of events in a trace
235 * the number of channels in the trace
237 public LttngKernelTraceGenerator(long duration
, long events
, int nbChannels
) {
238 fProcesses
= Arrays
.asList(sfProcesses
);
239 fDuration
= duration
;
241 fNbChans
= nbChannels
;
245 * Write the trace to a file
248 * the file to write the trace to
250 public void writeTrace(File file
) {
253 deleteDirectory(file
);
257 File metadataFile
= new File(file
.getPath() + File
.separator
+ "metadata");
258 File
[] streams
= new File
[fNbChans
];
259 FileChannel
[] channels
= new FileChannel
[fNbChans
];
262 for (int i
= 0; i
< fNbChans
; i
++) {
263 streams
[i
] = new File(file
.getPath() + File
.separator
+ "channel" + i
);
264 channels
[i
] = new FileOutputStream(streams
[i
]).getChannel();
266 } catch (FileNotFoundException e
) {
268 // determine the number of events per channel
269 long evPerChan
= fNbEvents
/ fNbChans
;
270 final int evPerPacket
= PacketWriter
.CONTENT_SIZE
/ EventWriter
.SIZE
;
271 long delta
= (int) (fDuration
/ evPerChan
);
273 Random rndLost
= new Random(1337);
274 for (int chan
= 0; chan
< fNbChans
; chan
++) {
275 int currentSpace
= 0;
276 ByteBuffer bb
= ByteBuffer
.allocate(65536);
277 bb
.order(ByteOrder
.LITTLE_ENDIAN
);
278 Random rnd
= new Random(1337);
279 int rnd0
= rnd
.nextInt(fProcesses
.size());
280 String prevComm
= fProcesses
.get(rnd0
);
281 int prevPID
= rnd0
+ chan
* fProcesses
.size();
288 int discardedTotal
= 0;
289 for (int eventNb
= 0; eventNb
< evPerChan
; eventNb
++) {
290 if (EventWriter
.SIZE
> currentSpace
) {
291 eventNb
+= discarded
;
293 long ts
= eventNb
* delta
+ delta
/ (fNbChans
+ 1) * chan
;
295 int pos
= rnd
.nextInt((int) (fProcesses
.size() * 1.5));
296 if (pos
>= fProcesses
.size()) {
299 while (pos
== prevPos
) {
300 pos
= rnd
.nextInt((int) (fProcesses
.size() * 1.5));
301 if (pos
>= fProcesses
.size()) {
305 String nextComm
= fProcesses
.get(pos
);
306 int nextPID
= pos
+ fProcesses
.size() * chan
;
311 if (EventWriter
.SIZE
> currentSpace
) {
313 for (int i
= 0; i
< currentSpace
; i
++) {
317 PacketWriter pw
= new PacketWriter(bb
);
320 int eventCount
= Math
.min(evPerPacket
, (int) evPerChan
- eventNb
);
321 discarded
= rndLost
.nextInt(10 * fNbChans
) == 0 ? rndLost
.nextInt(evPerPacket
) : 0;
322 discarded
= Math
.min(discarded
, (int) evPerChan
- eventNb
- eventCount
);
323 discardedTotal
+= discarded
;
324 long tsEnd
= (eventNb
+ eventCount
+ discarded
) * delta
;
325 pw
.writeNewHeader(tsBegin
, tsEnd
, chan
, eventCount
, discardedTotal
);
326 currentSpace
= PacketWriter
.CONTENT_SIZE
;
328 EventWriter ew
= new EventWriter(bb
);
329 int prev_state
= rnd
.nextInt(100);
330 if (prev_state
!= 0) {
333 final long shrunkenTimestamp
= ts
- offsetTime
;
334 final int tsMask
= (1 << 27) - 1;
335 if (shrunkenTimestamp
> ((1 << 27) + tsMask
)) {
336 /* allow only one compact timestamp overflow per packet */
337 throw new IllegalStateException("Invalid timestamp overflow:" + shrunkenTimestamp
);
339 final int clampedTs
= (int) (ts
& tsMask
);
340 int evSize
= ew
.writeEvent(clampedTs
, prevComm
, prevPID
, prevPrio
, prev_state
, nextComm
, nextPID
, nextPrio
);
341 currentSpace
-= evSize
;
345 if (bb
.position() > 63000) {
346 writeToDisk(channels
, chan
, bb
);
349 for (int i
= 0; i
< currentSpace
; i
++) {
352 writeToDisk(channels
, chan
, bb
);
354 channels
[chan
].close();
355 } catch (IOException e
) {
359 try (FileOutputStream fos
= new FileOutputStream(metadataFile
);) {
360 fos
.write(metadata
.getBytes());
361 } catch (IOException e
) {
365 private static void deleteDirectory(File directory
) {
367 Files
.walkFileTree(directory
.toPath(), new SimpleFileVisitor
<Path
>() {
369 public FileVisitResult
visitFile(Path file
, BasicFileAttributes attrs
) throws IOException
{
371 return FileVisitResult
.CONTINUE
;
375 public FileVisitResult
postVisitDirectory(Path dir
, IOException exc
) throws IOException
{
377 return FileVisitResult
.CONTINUE
;
380 } catch (IOException e
) {
385 private static void writeToDisk(FileChannel
[] channels
, int chan
, ByteBuffer bb
) {
388 channels
[chan
].write(bb
);
390 } catch (IOException e
) {
395 private class EventWriter
{
396 public static final int SIZE
=
405 private final ByteBuffer data
;
407 public EventWriter(ByteBuffer bb
) {
411 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
) {
412 byte[] bOut
= new byte[16];
413 byte[] bIn
= new byte[16];
414 byte[] temp
= prev_comm
.getBytes();
415 for (int i
= 0; i
< Math
.min(temp
.length
, 16); i
++) {
418 temp
= next_comm
.getBytes();
419 for (int i
= 0; i
< Math
.min(temp
.length
, 16); i
++) {
423 int timestamp
= ts
<< 5;
425 data
.putInt(timestamp
);
427 data
.putInt(prev_tid
);
428 data
.putInt(prev_prio
);
429 data
.putInt(prev_state
);
431 data
.putInt(next_tid
);
432 data
.putInt(next_prio
);
438 private class PacketWriter
{
439 private static final int SIZE
= 4096;
440 private static final int HEADER_SIZE
= 64;
441 private static final int CONTENT_SIZE
= SIZE
- HEADER_SIZE
;
443 private final ByteBuffer data
;
445 public PacketWriter(ByteBuffer bb
) {
449 public void writeNewHeader(long tsBegin
, long tsEnd
, int cpu
, int eventCount
, int discarded
) {
450 final int magicLE
= 0xC1FC1FC1;
452 0x11, 0x11, 0x11, 0x11,
453 0x11, 0x11, 0x11, 0x11,
454 0x11, 0x11, 0x11, 0x11,
455 0x11, 0x11, 0x11, 0x11 };
459 data
.putInt(magicLE
);
467 data
.putLong(tsBegin
);
473 data
.putLong((eventCount
* EventWriter
.SIZE
+ HEADER_SIZE
)* 8);
476 data
.putLong((SIZE
) * 8);
478 // events_discarded 4
479 data
.putInt(discarded
);