27df0b9e30e7c211657ec3ee55c5eb77e6ae1d20
[deliverable/tracecompass.git] / ctf / org.eclipse.tracecompass.ctf.core.tests / shared / org / eclipse / tracecompass / ctf / core / tests / shared / LttngKernelTraceGenerator.java
1 /*******************************************************************************
2 * Copyright (c) 2013, 2015 Ericsson
3 *
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
8 *
9 * Contributors:
10 * Matthew Khouzam - Initial API and implementation
11 * Marc-Andre Laperle - Move generation to traces folder
12 *******************************************************************************/
13
14 package org.eclipse.tracecompass.ctf.core.tests.shared;
15
16 import java.io.File;
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;
32
33 import org.eclipse.tracecompass.ctf.core.tests.CtfCoreTestPlugin;
34
35 /**
36 * Generate a kernel trace
37 *
38 * @author Matthew Khouzam
39 */
40 public class LttngKernelTraceGenerator {
41
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" +
50 "\n" +
51 "trace {\n" +
52 " major = 1;\n" +
53 " minor = 8;\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" +
60 " };\n" +
61 "};\n" +
62 "\n" +
63 "env {\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" +
73 "};\n" +
74 "\n" +
75 "clock {\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" +
82 "};\n" +
83 "\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" +
88 "\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" +
93 "\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" +
98 "\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" +
106 "};\n" +
107 "\n" +
108 "struct event_header_compact {\n" +
109 " enum : uint5_t { compact = 0 ... 30, extended = 31 } id;\n" +
110 " variant <id> {\n" +
111 " struct {\n" +
112 " uint27_clock_monotonic_t timestamp;\n" +
113 " } compact;\n" +
114 " struct {\n" +
115 " uint32_t id;\n" +
116 " uint64_clock_monotonic_t timestamp;\n" +
117 " } extended;\n" +
118 " } v;\n" +
119 "} align(8);\n" +
120 "\n" +
121 "struct event_header_large {\n" +
122 " enum : uint16_t { compact = 0 ... 65534, extended = 65535 } id;\n" +
123 " variant <id> {\n" +
124 " struct {\n" +
125 " uint32_clock_monotonic_t timestamp;\n" +
126 " } compact;\n" +
127 " struct {\n" +
128 " uint32_t id;\n" +
129 " uint64_clock_monotonic_t timestamp;\n" +
130 " } extended;\n" +
131 " } v;\n" +
132 "} align(8);\n" +
133 "\n" +
134 "stream {\n" +
135 " id = 0;\n" +
136 " event.header := struct event_header_compact;\n" +
137 " packet.context := struct packet_context;\n" +
138 "};\n" +
139 "\n" +
140 "event {\n" +
141 " name = sched_switch;\n" +
142 " id = 0;\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" +
152 " };\n" +
153 "};\n" +
154 "\n";
155
156 private final List<String> fProcesses;
157 private final long fDuration;
158 private final long fNbEvents;
159 private final int fNbChans;
160
161 private static final String[] sfProcesses = {
162 "IDLE",
163 "gnuplot",
164 "starcraft 2:pt3",
165 "bash",
166 "smash",
167 "thrash",
168 "fireball",
169 "Half-life 3",
170 "ST: The game"
171 };
172
173
174 private static final String TRACES_DIRECTORY = "traces";
175 private static final String TRACE_NAME = "synthetic-trace";
176
177 /**
178 * Main, not always needed
179 *
180 * @param args
181 * args
182 */
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));
187 }
188
189 /**
190 * Gets the name of the trace (top directory name)
191 *
192 * @return the name of the trace
193 */
194 public static String getName() {
195 return TRACE_NAME;
196 }
197
198 /**
199 * Get the path
200 *
201 * @return the path
202 */
203 public static String getPath() {
204 CtfCoreTestPlugin plugin = CtfCoreTestPlugin.getDefault();
205 if (plugin == null) {
206 return null;
207 }
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();
213 }
214
215 /**
216 * Generate a trace
217 *
218 * @param file
219 * the file to write the trace to
220 */
221 public static void generateLttngKernelTrace(File file) {
222 final int cpus = 25;
223 LttngKernelTraceGenerator gt = new LttngKernelTraceGenerator(2l * Integer.MAX_VALUE - 100, 500000, cpus);
224 gt.writeTrace(file);
225 }
226
227 /**
228 * Make a kernel trace
229 *
230 * @param duration
231 * the duration of the trace
232 * @param events
233 * the number of events in a trace
234 * @param nbChannels
235 * the number of channels in the trace
236 */
237 public LttngKernelTraceGenerator(long duration, long events, int nbChannels) {
238 fProcesses = Arrays.asList(sfProcesses);
239 fDuration = duration;
240 fNbEvents = events;
241 fNbChans = nbChannels;
242 }
243
244 /**
245 * Write the trace to a file
246 *
247 * @param file
248 * the file to write the trace to
249 */
250 public void writeTrace(File file) {
251
252 if (file.exists()) {
253 deleteDirectory(file);
254 }
255 file.mkdir();
256
257 File metadataFile = new File(file.getPath() + File.separator + "metadata");
258 File[] streams = new File[fNbChans];
259 FileChannel[] channels = new FileChannel[fNbChans];
260
261 try {
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();
265 }
266 } catch (FileNotFoundException e) {
267 }
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);
272 long offsetTime = 0;
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();
282 if (rnd0 == 0) {
283 prevPID = 0;
284 }
285 int prevPrio = 0;
286 int prevPos = -1;
287 int discarded = 0;
288 int discardedTotal = 0;
289 for (int eventNb = 0; eventNb < evPerChan; eventNb++) {
290 if (EventWriter.SIZE > currentSpace) {
291 eventNb += discarded;
292 }
293 long ts = eventNb * delta + delta / (fNbChans + 1) * chan;
294
295 int pos = rnd.nextInt((int) (fProcesses.size() * 1.5));
296 if (pos >= fProcesses.size()) {
297 pos = 0;
298 }
299 while (pos == prevPos) {
300 pos = rnd.nextInt((int) (fProcesses.size() * 1.5));
301 if (pos >= fProcesses.size()) {
302 pos = 0;
303 }
304 }
305 String nextComm = fProcesses.get(pos);
306 int nextPID = pos + fProcesses.size() * chan;
307 if (pos == 0) {
308 nextPID = 0;
309 }
310 int nextPrio = 0;
311 if (EventWriter.SIZE > currentSpace) {
312 // pad to end
313 for (int i = 0; i < currentSpace; i++) {
314 bb.put((byte) 0x00);
315 }
316 // write new packet
317 PacketWriter pw = new PacketWriter(bb);
318 long tsBegin = ts;
319 offsetTime = ts;
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;
327 }
328 EventWriter ew = new EventWriter(bb);
329 int prev_state = rnd.nextInt(100);
330 if (prev_state != 0) {
331 prev_state = 1;
332 }
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);
338 }
339 final int clampedTs = (int) (ts & tsMask);
340 int evSize = ew.writeEvent(clampedTs, prevComm, prevPID, prevPrio, prev_state, nextComm, nextPID, nextPrio);
341 currentSpace -= evSize;
342 prevComm = nextComm;
343 prevPID = nextPID;
344 prevPrio = nextPrio;
345 if (bb.position() > 63000) {
346 writeToDisk(channels, chan, bb);
347 }
348 }
349 for (int i = 0; i < currentSpace; i++) {
350 bb.put((byte) 0x00);
351 }
352 writeToDisk(channels, chan, bb);
353 try {
354 channels[chan].close();
355 } catch (IOException e) {
356 e.printStackTrace();
357 }
358 }
359 try (FileOutputStream fos = new FileOutputStream(metadataFile);) {
360 fos.write(metadata.getBytes());
361 } catch (IOException e) {
362 }
363 }
364
365 private static void deleteDirectory(File directory) {
366 try {
367 Files.walkFileTree(directory.toPath(), new SimpleFileVisitor<Path>() {
368 @Override
369 public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
370 Files.delete(file);
371 return FileVisitResult.CONTINUE;
372 }
373
374 @Override
375 public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
376 Files.delete(dir);
377 return FileVisitResult.CONTINUE;
378 }
379 });
380 } catch (IOException e) {
381 e.printStackTrace();
382 }
383 }
384
385 private static void writeToDisk(FileChannel[] channels, int chan, ByteBuffer bb) {
386 try {
387 bb.flip();
388 channels[chan].write(bb);
389 bb.clear();
390 } catch (IOException e) {
391 e.printStackTrace();
392 }
393 }
394
395 private class EventWriter {
396 public static final int SIZE =
397 4 + // timestamp
398 16 + // prev_comm
399 4 + // prev_tid
400 4 + // prev_prio
401 4 + // prev_state
402 16 + // current_comm
403 4 + // next_tid
404 4; // next_prio
405 private final ByteBuffer data;
406
407 public EventWriter(ByteBuffer bb) {
408 data = bb;
409 }
410
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++) {
416 bOut[i] = temp[i];
417 }
418 temp = next_comm.getBytes();
419 for (int i = 0; i < Math.min(temp.length, 16); i++) {
420 bIn[i] = temp[i];
421 }
422
423 int timestamp = ts << 5;
424
425 data.putInt(timestamp);
426 data.put(bOut);
427 data.putInt(prev_tid);
428 data.putInt(prev_prio);
429 data.putInt(prev_state);
430 data.put(bIn);
431 data.putInt(next_tid);
432 data.putInt(next_prio);
433 return SIZE;
434 }
435
436 }
437
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;
442
443 private final ByteBuffer data;
444
445 public PacketWriter(ByteBuffer bb) {
446 data = bb;
447 }
448
449 public void writeNewHeader(long tsBegin, long tsEnd, int cpu, int eventCount, int discarded) {
450 final int magicLE = 0xC1FC1FC1;
451 byte uuid[] = {
452 0x11, 0x11, 0x11, 0x11,
453 0x11, 0x11, 0x11, 0x11,
454 0x11, 0x11, 0x11, 0x11,
455 0x11, 0x11, 0x11, 0x11 };
456 // packet header
457
458 // magic number 4
459 data.putInt(magicLE);
460 // uuid 16
461 data.put(uuid);
462 // stream ID 4
463 data.putInt(0);
464
465 // packet context
466 // timestamp_begin 8
467 data.putLong(tsBegin);
468
469 // timestamp_end 8
470 data.putLong(tsEnd);
471
472 // content_size 8
473 data.putLong((eventCount * EventWriter.SIZE + HEADER_SIZE)* 8);
474
475 // packet_size 8
476 data.putLong((SIZE) * 8);
477
478 // events_discarded 4
479 data.putInt(discarded);
480
481 // cpu_id 4
482 data.putInt(cpu);
483
484 }
485
486 }
487
488
489
490 }
This page took 0.04072 seconds and 4 git commands to generate.