ctf: Move plugins to the Trace Compass namespace
[deliverable/tracecompass.git] / org.eclipse.tracecompass.ctf.core.tests / src / org / eclipse / linuxtools / ctf / core / tests / synthetictraces / LttngKernelTraceGenerator.java
1 /*******************************************************************************
2 * Copyright (c) 2013, 2014 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.linuxtools.ctf.core.tests.synthetictraces;
15
16 import java.io.File;
17 import java.io.FileNotFoundException;
18 import java.io.FileOutputStream;
19 import java.io.IOException;
20 import java.net.URL;
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;
27
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;
32
33 /**
34 * Generate a kernel trace
35 *
36 * @author Matthew Khouzam
37 */
38 public class LttngKernelTraceGenerator {
39
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" +
48 "\n" +
49 "trace {\n" +
50 " major = 1;\n" +
51 " minor = 8;\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" +
58 " };\n" +
59 "};\n" +
60 "\n" +
61 "env {\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" +
71 "};\n" +
72 "\n" +
73 "clock {\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" +
80 "};\n" +
81 "\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" +
86 "\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" +
91 "\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" +
96 "\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" +
104 "};\n" +
105 "\n" +
106 "struct event_header_compact {\n" +
107 " enum : uint5_t { compact = 0 ... 30, extended = 31 } id;\n" +
108 " variant <id> {\n" +
109 " struct {\n" +
110 " uint27_clock_monotonic_t timestamp;\n" +
111 " } compact;\n" +
112 " struct {\n" +
113 " uint32_t id;\n" +
114 " uint64_clock_monotonic_t timestamp;\n" +
115 " } extended;\n" +
116 " } v;\n" +
117 "} align(8);\n" +
118 "\n" +
119 "struct event_header_large {\n" +
120 " enum : uint16_t { compact = 0 ... 65534, extended = 65535 } id;\n" +
121 " variant <id> {\n" +
122 " struct {\n" +
123 " uint32_clock_monotonic_t timestamp;\n" +
124 " } compact;\n" +
125 " struct {\n" +
126 " uint32_t id;\n" +
127 " uint64_clock_monotonic_t timestamp;\n" +
128 " } extended;\n" +
129 " } v;\n" +
130 "} align(8);\n" +
131 "\n" +
132 "stream {\n" +
133 " id = 0;\n" +
134 " event.header := struct event_header_compact;\n" +
135 " packet.context := struct packet_context;\n" +
136 "};\n" +
137 "\n" +
138 "event {\n" +
139 " name = sched_switch;\n" +
140 " id = 0;\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" +
150 " };\n" +
151 "};\n" +
152 "\n";
153
154 private final List<String> fProcesses;
155 private final long fDuration;
156 private final long fNbEvents;
157 private final int fNbChans;
158
159 private static final String[] sfProcesses = {
160 "IDLE",
161 "gnuplot",
162 "starcraft 2:pt3",
163 "bash",
164 "smash",
165 "thrash",
166 "fireball",
167 "Half-life 3",
168 "ST: The game"
169 };
170
171
172 private static final String TRACES_DIRECTORY = "traces";
173 private static final String TRACE_NAME = "synthetic-trace";
174
175 /**
176 * Main, not always needed
177 *
178 * @param args
179 * args
180 */
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));
185 }
186
187 /**
188 * Gets the name of the trace (top directory name)
189 *
190 * @return the name of the trace
191 */
192 public static String getName() {
193 return TRACE_NAME;
194 }
195
196 /**
197 * Get the path
198 *
199 * @return the path
200 */
201 public static String getPath() {
202 CtfCoreTestPlugin plugin = CtfCoreTestPlugin.getDefault();
203 if (plugin == null) {
204 return null;
205 }
206 URL location = FileLocator.find(plugin.getBundle(), new Path(TRACES_DIRECTORY), null);
207 File file = null;
208 try {
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();
214 }
215
216 if (!file.exists()) {
217 generateLttngKernelTrace(file);
218 }
219 return file.getAbsolutePath();
220 }
221
222 /**
223 * Generate a trace
224 *
225 * @param file
226 * the file to write the trace to
227 */
228 public static void generateLttngKernelTrace(File file) {
229 final int cpus = 25;
230 LttngKernelTraceGenerator gt = new LttngKernelTraceGenerator(2l * Integer.MAX_VALUE - 100, 500000, cpus);
231 gt.writeTrace(file);
232 }
233
234 /**
235 * Make a kernel trace
236 *
237 * @param duration
238 * the duration of the trace
239 * @param events
240 * the number of events in a trace
241 * @param nbChannels
242 * the number of channels in the trace
243 */
244 public LttngKernelTraceGenerator(long duration, long events, int nbChannels) {
245 fProcesses = Arrays.asList(sfProcesses);
246 fDuration = duration;
247 fNbEvents = events;
248 fNbChans = nbChannels;
249 }
250
251 /**
252 * Write the trace to a file
253 *
254 * @param file
255 * the file to write the trace to
256 */
257 public void writeTrace(File file) {
258
259 if (!file.exists()) {
260 file.mkdir();
261 } else {
262 if (file.isFile()) {
263 file.delete();
264 file.mkdir();
265 } else {
266 // the ctf parser doesn't recurse, so we don't need to.
267 final File[] listFiles = file.listFiles();
268 for (File child : listFiles) {
269 child.delete();
270 }
271 }
272 }
273
274 File metadataFile = new File(file.getPath() + File.separator + "metadata");
275 File[] streams = new File[fNbChans];
276 FileChannel[] channels = new FileChannel[fNbChans];
277
278 try {
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();
282 }
283 } catch (FileNotFoundException e) {
284 }
285 // determine the number of events per channel
286 long evPerChan = fNbEvents / fNbChans;
287 int delta = (int) (fDuration / evPerChan);
288 int offsetTime = 0;
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();
297 if (rnd0 == 0) {
298 prevPID = 0;
299 }
300 int prevPrio = 0;
301 int prevPos = -1;
302 for (int eventNb = 0; eventNb < evPerChan; eventNb++) {
303 int ts = eventNb * delta + delta / (fNbChans + 1) * chan;
304
305 int pos = rnd.nextInt((int) (fProcesses.size() * 1.5));
306 if (pos >= fProcesses.size()) {
307 pos = 0;
308 }
309 while (pos == prevPos) {
310 pos = rnd.nextInt((int) (fProcesses.size() * 1.5));
311 if (pos >= fProcesses.size()) {
312 pos = 0;
313 }
314 }
315 String nextComm = fProcesses.get(pos);
316 int nextPID = pos + fProcesses.size() * chan;
317 if (pos == 0) {
318 nextPID = 0;
319 }
320 int nextPrio = 0;
321 if (EventWriter.SIZE > currentSpace) {
322 // pad to end
323 for (int i = 0; i < currentSpace; i++) {
324 bb.put((byte) 0x00);
325 }
326 // write new packet
327 PacketWriter pw = new PacketWriter(bb);
328 int tsBegin = ts;
329 offsetTime = ts;
330 int tsEnd = (eventNb + (PacketWriter.SIZE / EventWriter.SIZE)) * delta + 1;
331 pw.writeNewHeader(tsBegin, tsEnd, chan);
332 currentSpace = PacketWriter.CONTENT_SIZE;
333 }
334 EventWriter ew = new EventWriter(bb);
335 int prev_state = rnd.nextInt(100);
336 if (prev_state != 0) {
337 prev_state = 1;
338 }
339 final int shrunkenTimestamp = ts - offsetTime;
340 final int tsMask = (1 << 27) - 1;
341 if (shrunkenTimestamp > ((1 << 27) + tsMask)) {
342 new Object();
343 System.err.println("PROBLEM");
344 }
345 final int clampedTs = ts & tsMask;
346 int evSize = ew.writeEvent(clampedTs, prevComm, prevPID, prevPrio, prev_state, nextComm, nextPID, nextPrio);
347 currentSpace -= evSize;
348 prevComm = nextComm;
349 prevPID = nextPID;
350 prevPrio = nextPrio;
351 if (bb.position() > 63000) {
352 writeToDisk(channels, chan, bb);
353 }
354 }
355 for (int i = 0; i < currentSpace; i++) {
356 bb.put((byte) 0x00);
357 }
358 writeToDisk(channels, chan, bb);
359 try {
360 channels[chan].close();
361 } catch (IOException e) {
362 e.printStackTrace();
363 }
364 }
365 try (FileOutputStream fos = new FileOutputStream(metadataFile);) {
366 fos.write(metadata.getBytes());
367 } catch (IOException e) {
368 }
369 }
370
371 private static void writeToDisk(FileChannel[] channels, int chan, ByteBuffer bb) {
372 try {
373 bb.flip();
374 channels[chan].write(bb);
375 bb.clear();
376 } catch (IOException e) {
377 e.printStackTrace();
378 }
379 }
380
381 private class EventWriter {
382 public static final int SIZE =
383 4 + // timestamp
384 16 + // prev_comm
385 4 + // prev_tid
386 4 + // prev_prio
387 4 + // prev_state
388 16 + // current_comm
389 4 + // next_tid
390 4; // next_prio
391 private final ByteBuffer data;
392
393 public EventWriter(ByteBuffer bb) {
394 data = bb;
395 }
396
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++) {
402 bOut[i] = temp[i];
403 }
404 temp = next_comm.getBytes();
405 for (int i = 0; i < Math.min(temp.length, 16); i++) {
406 bIn[i] = temp[i];
407 }
408
409 int timestamp = ts << 5;
410
411 data.putInt(timestamp);
412 data.put(bOut);
413 data.putInt(prev_tid);
414 data.putInt(prev_prio);
415 data.putInt(prev_state);
416 data.put(bIn);
417 data.putInt(next_tid);
418 data.putInt(next_prio);
419 return SIZE;
420 }
421
422 }
423
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;
428
429 private final ByteBuffer data;
430
431 public PacketWriter(ByteBuffer bb) {
432 data = bb;
433 }
434
435 public void writeNewHeader(int tsBegin, int tsEnd, int cpu) {
436 final int magicLE = 0xC1FC1FC1;
437 byte uuid[] = {
438 0x11, 0x11, 0x11, 0x11,
439 0x11, 0x11, 0x11, 0x11,
440 0x11, 0x11, 0x11, 0x11,
441 0x11, 0x11, 0x11, 0x11 };
442 // packet header
443
444 // magic number 4
445 data.putInt(magicLE);
446 // uuid 16
447 data.put(uuid);
448 // stream ID 4
449 data.putInt(0);
450
451 // packet context
452 // timestamp_begin 8
453 data.putLong(tsBegin);
454
455 // timestamp_end 8
456 data.putLong(tsEnd);
457
458 // content_size 8
459 data.putLong((CONTENT_SIZE / EventWriter.SIZE * EventWriter.SIZE + HEADER_SIZE) * 8);
460
461 // packet_size 8
462 data.putLong((SIZE) * 8);
463
464 // events_discarded 4
465 data.putInt(0);
466
467 // cpu_id 4
468 data.putInt(cpu);
469
470 }
471
472 }
473
474
475
476 }
This page took 0.04322 seconds and 5 git commands to generate.