1 /*******************************************************************************
2 * Copyright (c) 2013 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
= System
.getProperty("java.io.tmpdir") + 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
];
277 FileOutputStream fos
= null;
280 for (int i
= 0; i
< fNbChans
; i
++) {
281 streams
[i
] = new File(file
.getPath() + File
.separator
+ "channel" + i
);
282 channels
[i
] = new FileOutputStream(streams
[i
]).getChannel();
284 } catch (FileNotFoundException e
) {
286 // determine the number of events per channel
287 long evPerChan
= fNbEvents
/ fNbChans
;
288 int delta
= (int) (fDuration
/ evPerChan
);
290 for (int chan
= 0; chan
< fNbChans
; chan
++) {
291 int currentSpace
= 0;
292 ByteBuffer bb
= ByteBuffer
.allocate(65536);
293 bb
.order(ByteOrder
.LITTLE_ENDIAN
);
294 Random rnd
= new Random(1337);
295 int rnd0
= rnd
.nextInt(fProcesses
.size());
296 String prevComm
= fProcesses
.get(rnd0
);
297 int prevPID
= rnd0
+ chan
* fProcesses
.size();
303 for (int eventNb
= 0; eventNb
< evPerChan
; eventNb
++) {
304 int ts
= eventNb
* delta
+ delta
/ (fNbChans
+ 1) * chan
;
306 int pos
= rnd
.nextInt((int) (fProcesses
.size() * 1.5));
307 if (pos
>= fProcesses
.size()) {
310 while (pos
== prevPos
) {
311 pos
= rnd
.nextInt((int) (fProcesses
.size() * 1.5));
312 if (pos
>= fProcesses
.size()) {
316 String nextComm
= fProcesses
.get(pos
);
317 int nextPID
= pos
+ fProcesses
.size() * chan
;
322 if (EventWriter
.SIZE
> currentSpace
) {
324 for (int i
= 0; i
< currentSpace
; i
++) {
328 PacketWriter pw
= new PacketWriter(bb
);
331 int tsEnd
= (eventNb
+ (PacketWriter
.SIZE
/ EventWriter
.SIZE
)) * delta
+ 1;
332 pw
.writeNewHeader(tsBegin
, tsEnd
, chan
);
333 currentSpace
= PacketWriter
.CONTENT_SIZE
;
335 EventWriter ew
= new EventWriter(bb
);
336 int prev_state
= rnd
.nextInt(100);
337 if (prev_state
!= 0) {
340 final int shrunkenTimestamp
= ts
- offsetTime
;
341 final int tsMask
= (1 << 27) - 1;
342 if (shrunkenTimestamp
> ((1 << 27) + tsMask
)) {
344 System
.err
.println("PROBLEM");
346 final int clampedTs
= ts
& tsMask
;
347 int evSize
= ew
.writeEvent(clampedTs
, prevComm
, prevPID
, prevPrio
, prev_state
, nextComm
, nextPID
, nextPrio
);
348 currentSpace
-= evSize
;
352 if (bb
.position() > 63000) {
353 writeToDisk(channels
, chan
, bb
);
356 for (int i
= 0; i
< currentSpace
; i
++) {
359 writeToDisk(channels
, chan
, bb
);
361 channels
[chan
].close();
362 } catch (IOException e
) {
367 fos
= new FileOutputStream(metadataFile
);
368 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(int tsBegin
, int tsEnd
, int cpu
) {
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((CONTENT_SIZE
/ EventWriter
.SIZE
* EventWriter
.SIZE
+ HEADER_SIZE
) * 8);
465 data
.putLong((SIZE
) * 8);
467 // events_discarded 4