1 /*******************************************************************************
2 * Copyright (c) 2013, 2014 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
.linuxtools
.ctf
.core
.tests
.synthetictraces
;
17 import java
.io
.FileNotFoundException
;
18 import java
.io
.FileOutputStream
;
19 import java
.io
.IOException
;
21 import java
.nio
.ByteBuffer
;
22 import java
.nio
.ByteOrder
;
23 import java
.nio
.channels
.FileChannel
;
24 import java
.util
.Arrays
;
25 import java
.util
.List
;
26 import java
.util
.Random
;
28 import org
.eclipse
.core
.runtime
.FileLocator
;
29 import org
.eclipse
.core
.runtime
.IPath
;
30 import org
.eclipse
.core
.runtime
.Path
;
31 import org
.eclipse
.linuxtools
.ctf
.core
.tests
.CtfCoreTestPlugin
;
34 * Generate a kernel trace
36 * @author Matthew Khouzam
38 public class LttngKernelTraceGenerator
{
40 private static final String metadata
= "/* CTF 1.8 */ \n" +
41 "typealias integer { size = 8; align = 8; signed = false; } := uint8_t;\n" +
42 "typealias integer { size = 16; align = 8; signed = false; } := uint16_t;\n" +
43 "typealias integer { size = 32; align = 8; signed = false; } := uint32_t;\n" +
44 "typealias integer { size = 64; align = 8; signed = false; } := uint64_t;\n" +
45 "typealias integer { size = 32; align = 8; signed = false; } := unsigned long;\n" +
46 "typealias integer { size = 5; align = 1; signed = false; } := uint5_t;\n" +
47 "typealias integer { size = 27; align = 1; signed = false; } := uint27_t;\n" +
52 " uuid = \"11111111-1111-1111-1111-111111111111\";\n" +
53 " byte_order = le;\n" +
54 " packet.header := struct {\n" +
55 " uint32_t magic;\n" +
56 " uint8_t uuid[16];\n" +
57 " uint32_t stream_id;\n" +
62 " hostname = \"synthetic-host\";\n" +
63 " domain = \"kernel\";\n" +
64 " sysname = \"FakeLinux\";\n" +
65 " kernel_release = \"1.0\";\n" +
66 " kernel_version = \"Fake Os Synthetic Trace\";\n" +
67 " tracer_name = \"lttng-modules\";\n" +
68 " tracer_major = 2;\n" +
69 " tracer_minor = 1;\n" +
70 " tracer_patchlevel = 0;\n" +
74 " name = monotonic;\n" +
75 " uuid = \"bbff68f0-c633-4ea1-92cd-bd11024ec4de\";\n" +
76 " description = \"Monotonic Clock\";\n" +
77 " freq = 1000000000; /* Frequency, in Hz */\n" +
78 " /* clock value offset from Epoch is: offset * (1/freq) */\n" +
79 " offset = 1368000272650993664;\n" +
82 "typealias integer {\n" +
83 " size = 27; align = 1; signed = false;\n" +
84 " map = clock.monotonic.value;\n" +
85 "} := uint27_clock_monotonic_t;\n" +
87 "typealias integer {\n" +
88 " size = 32; align = 8; signed = false;\n" +
89 " map = clock.monotonic.value;\n" +
90 "} := uint32_clock_monotonic_t;\n" +
92 "typealias integer {\n" +
93 " size = 64; align = 8; signed = false;\n" +
94 " map = clock.monotonic.value;\n" +
95 "} := uint64_clock_monotonic_t;\n" +
97 "struct packet_context {\n" +
98 " uint64_clock_monotonic_t timestamp_begin;\n" +
99 " uint64_clock_monotonic_t timestamp_end;\n" +
100 " uint64_t content_size;\n" +
101 " uint64_t packet_size;\n" +
102 " unsigned long events_discarded;\n" +
103 " uint32_t cpu_id;\n" +
106 "struct event_header_compact {\n" +
107 " enum : uint5_t { compact = 0 ... 30, extended = 31 } id;\n" +
108 " variant <id> {\n" +
110 " uint27_clock_monotonic_t timestamp;\n" +
114 " uint64_clock_monotonic_t timestamp;\n" +
119 "struct event_header_large {\n" +
120 " enum : uint16_t { compact = 0 ... 65534, extended = 65535 } id;\n" +
121 " variant <id> {\n" +
123 " uint32_clock_monotonic_t timestamp;\n" +
127 " uint64_clock_monotonic_t timestamp;\n" +
134 " event.header := struct event_header_compact;\n" +
135 " packet.context := struct packet_context;\n" +
139 " name = sched_switch;\n" +
141 " stream_id = 0;\n" +
142 " fields := struct {\n" +
143 " integer { size = 8; align = 8; signed = 1; encoding = UTF8; base = 10; } _prev_comm[16];\n" +
144 " integer { size = 32; align = 8; signed = 1; encoding = none; base = 10; } _prev_tid;\n" +
145 " integer { size = 32; align = 8; signed = 1; encoding = none; base = 10; } _prev_prio;\n" +
146 " integer { size = 32; align = 8; signed = 1; encoding = none; base = 10; } _prev_state;\n" +
147 " integer { size = 8; align = 8; signed = 1; encoding = UTF8; base = 10; } _next_comm[16];\n" +
148 " integer { size = 32; align = 8; signed = 1; encoding = none; base = 10; } _next_tid;\n" +
149 " integer { size = 32; align = 8; signed = 1; encoding = none; base = 10; } _next_prio;\n" +
154 private final List
<String
> fProcesses
;
155 private final long fDuration
;
156 private final long fNbEvents
;
157 private final int fNbChans
;
159 private static final String
[] sfProcesses
= {
172 private static final String TRACES_DIRECTORY
= "traces";
173 private static final String TRACE_NAME
= "synthetic-trace";
176 * Main, not always needed
181 public static void main(String
[] args
) {
182 // not using createTempFile as this is a directory
183 String path
= CtfCoreTestPlugin
.getTemporaryDirPath() + File
.separator
+ TRACE_NAME
;
184 generateLttngKernelTrace(new File(path
));
188 * Gets the name of the trace (top directory name)
190 * @return the name of the trace
192 public static String
getName() {
201 public static String
getPath() {
202 CtfCoreTestPlugin plugin
= CtfCoreTestPlugin
.getDefault();
203 if (plugin
== null) {
206 URL location
= FileLocator
.find(plugin
.getBundle(), new Path(TRACES_DIRECTORY
), null);
209 IPath path
= new Path(FileLocator
.toFileURL(location
).getPath()).append(TRACE_NAME
);
210 file
= path
.toFile();
211 } catch (IOException e
) {
212 // Shouldn't happen but at least throw something to get the test to fail early
213 throw new IllegalStateException();
216 if (!file
.exists()) {
217 generateLttngKernelTrace(file
);
219 return file
.getAbsolutePath();
226 * the file to write the trace to
228 public static void generateLttngKernelTrace(File file
) {
230 LttngKernelTraceGenerator gt
= new LttngKernelTraceGenerator(2l * Integer
.MAX_VALUE
- 100, 500000, cpus
);
235 * Make a kernel trace
238 * the duration of the trace
240 * the number of events in a trace
242 * the number of channels in the trace
244 public LttngKernelTraceGenerator(long duration
, long events
, int nbChannels
) {
245 fProcesses
= Arrays
.asList(sfProcesses
);
246 fDuration
= duration
;
248 fNbChans
= nbChannels
;
252 * Write the trace to a file
255 * the file to write the trace to
257 public void writeTrace(File file
) {
259 if (!file
.exists()) {
266 // the ctf parser doesn't recurse, so we don't need to.
267 final File
[] listFiles
= file
.listFiles();
268 for (File child
: listFiles
) {
274 File metadataFile
= new File(file
.getPath() + File
.separator
+ "metadata");
275 File
[] streams
= new File
[fNbChans
];
276 FileChannel
[] channels
= new FileChannel
[fNbChans
];
279 for (int i
= 0; i
< fNbChans
; i
++) {
280 streams
[i
] = new File(file
.getPath() + File
.separator
+ "channel" + i
);
281 channels
[i
] = new FileOutputStream(streams
[i
]).getChannel();
283 } catch (FileNotFoundException e
) {
285 // determine the number of events per channel
286 long evPerChan
= fNbEvents
/ fNbChans
;
287 int delta
= (int) (fDuration
/ evPerChan
);
289 for (int chan
= 0; chan
< fNbChans
; chan
++) {
290 int currentSpace
= 0;
291 ByteBuffer bb
= ByteBuffer
.allocate(65536);
292 bb
.order(ByteOrder
.LITTLE_ENDIAN
);
293 Random rnd
= new Random(1337);
294 int rnd0
= rnd
.nextInt(fProcesses
.size());
295 String prevComm
= fProcesses
.get(rnd0
);
296 int prevPID
= rnd0
+ chan
* fProcesses
.size();
302 for (int eventNb
= 0; eventNb
< evPerChan
; eventNb
++) {
303 int ts
= eventNb
* delta
+ delta
/ (fNbChans
+ 1) * chan
;
305 int pos
= rnd
.nextInt((int) (fProcesses
.size() * 1.5));
306 if (pos
>= fProcesses
.size()) {
309 while (pos
== prevPos
) {
310 pos
= rnd
.nextInt((int) (fProcesses
.size() * 1.5));
311 if (pos
>= fProcesses
.size()) {
315 String nextComm
= fProcesses
.get(pos
);
316 int nextPID
= pos
+ fProcesses
.size() * chan
;
321 if (EventWriter
.SIZE
> currentSpace
) {
323 for (int i
= 0; i
< currentSpace
; i
++) {
327 PacketWriter pw
= new PacketWriter(bb
);
330 int tsEnd
= (eventNb
+ (PacketWriter
.SIZE
/ EventWriter
.SIZE
)) * delta
+ 1;
331 pw
.writeNewHeader(tsBegin
, tsEnd
, chan
);
332 currentSpace
= PacketWriter
.CONTENT_SIZE
;
334 EventWriter ew
= new EventWriter(bb
);
335 int prev_state
= rnd
.nextInt(100);
336 if (prev_state
!= 0) {
339 final int shrunkenTimestamp
= ts
- offsetTime
;
340 final int tsMask
= (1 << 27) - 1;
341 if (shrunkenTimestamp
> ((1 << 27) + tsMask
)) {
343 System
.err
.println("PROBLEM");
345 final int clampedTs
= ts
& tsMask
;
346 int evSize
= ew
.writeEvent(clampedTs
, prevComm
, prevPID
, prevPrio
, prev_state
, nextComm
, nextPID
, nextPrio
);
347 currentSpace
-= evSize
;
351 if (bb
.position() > 63000) {
352 writeToDisk(channels
, chan
, bb
);
355 for (int i
= 0; i
< currentSpace
; i
++) {
358 writeToDisk(channels
, chan
, bb
);
360 channels
[chan
].close();
361 } catch (IOException e
) {
365 try (FileOutputStream fos
= new FileOutputStream(metadataFile
);) {
366 fos
.write(metadata
.getBytes());
367 } catch (IOException e
) {
371 private static void writeToDisk(FileChannel
[] channels
, int chan
, ByteBuffer bb
) {
374 channels
[chan
].write(bb
);
376 } catch (IOException e
) {
381 private class EventWriter
{
382 public static final int SIZE
=
391 private final ByteBuffer data
;
393 public EventWriter(ByteBuffer bb
) {
397 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
) {
398 byte[] bOut
= new byte[16];
399 byte[] bIn
= new byte[16];
400 byte[] temp
= prev_comm
.getBytes();
401 for (int i
= 0; i
< Math
.min(temp
.length
, 16); i
++) {
404 temp
= next_comm
.getBytes();
405 for (int i
= 0; i
< Math
.min(temp
.length
, 16); i
++) {
409 int timestamp
= ts
<< 5;
411 data
.putInt(timestamp
);
413 data
.putInt(prev_tid
);
414 data
.putInt(prev_prio
);
415 data
.putInt(prev_state
);
417 data
.putInt(next_tid
);
418 data
.putInt(next_prio
);
424 private class PacketWriter
{
425 private static final int SIZE
= 4096;
426 private static final int HEADER_SIZE
= 64;
427 private static final int CONTENT_SIZE
= SIZE
- HEADER_SIZE
;
429 private final ByteBuffer data
;
431 public PacketWriter(ByteBuffer bb
) {
435 public void writeNewHeader(int tsBegin
, int tsEnd
, int cpu
) {
436 final int magicLE
= 0xC1FC1FC1;
438 0x11, 0x11, 0x11, 0x11,
439 0x11, 0x11, 0x11, 0x11,
440 0x11, 0x11, 0x11, 0x11,
441 0x11, 0x11, 0x11, 0x11 };
445 data
.putInt(magicLE
);
453 data
.putLong(tsBegin
);
459 data
.putLong((CONTENT_SIZE
/ EventWriter
.SIZE
* EventWriter
.SIZE
+ HEADER_SIZE
) * 8);
462 data
.putLong((SIZE
) * 8);
464 // events_discarded 4