| /******************************************************************************* |
| * Copyright (c) 2013, 2014 Ericsson |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License 2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * Matthew Khouzam - Initial API and implementation |
| * Simon Delisle - Generate dummy trace |
| *******************************************************************************/ |
| |
| package org.eclipse.tracecompass.ctf.core.tests.trace; |
| |
| import static org.junit.Assert.assertEquals; |
| import static org.junit.Assert.assertNotNull; |
| |
| import java.io.File; |
| import java.io.FileOutputStream; |
| import java.io.FileWriter; |
| import java.io.IOException; |
| import java.nio.ByteBuffer; |
| import java.nio.ByteOrder; |
| import java.util.ArrayList; |
| import java.util.List; |
| |
| import org.eclipse.tracecompass.ctf.core.CTFException; |
| import org.eclipse.tracecompass.ctf.core.event.IEventDeclaration; |
| import org.eclipse.tracecompass.ctf.core.tests.CtfCoreTestPlugin; |
| import org.eclipse.tracecompass.ctf.core.trace.CTFTrace; |
| import org.eclipse.tracecompass.internal.ctf.core.event.EventDeclaration; |
| import org.eclipse.tracecompass.internal.ctf.core.event.metadata.IOStructGen; |
| import org.junit.Test; |
| |
| /** |
| * Unit tests for {@link IOStructGen} |
| * |
| * @author Matthew Khouzam |
| */ |
| public class IOstructgenTest { |
| |
| private static final String metadataDecs = "typealias integer { size = 8; align = 8; signed = false; } := uint8_t;\n" |
| + "typealias integer { size = 16; align = 8; signed = false; } := uint16_t;\n" |
| + "typealias integer { size = 32; align = 8; signed = false; } := uint32_t;\n" |
| + "typealias integer { size = 64; align = 8; signed = false; } := uint64_t;\n" |
| + "typealias integer { size = 64; align = 8; signed = false; } := unsigned long;\n" |
| + "typealias integer { size = 5; align = 1; signed = false; } := uint5_t;\n" |
| + "typealias integer { size = 27; align = 1; signed = false; } := uint27_t;\n" |
| + "typealias integer { size = 32; align = 1; signed = true; base = decimal; } := int32_t;\n" |
| + "typealias integer { size = 31; align = 1; signed = true; base = dec; } := int31_t;\n" |
| + "typealias integer { size = 30; align = 1; signed = true; base = d; } := int30_t;\n" |
| + "typealias integer { size = 29; align = 1; signed = true; base = i; } := int29_t;\n" |
| + "typealias integer { size = 28; align = 1; signed = true; base = u; } := int28_t;\n" |
| + "typealias integer { size = 27; align = 1; signed = true; base = hexadecimal; } := int27_t;\n" |
| + "typealias integer { size = 26; align = 1; signed = true; base = hex; } := int26_t;\n" |
| + "typealias integer { size = 25; align = 1; signed = true; base = x; } := int25_t;\n" |
| + "typealias integer { size = 24; align = 1; signed = true; base = X; } := int24_t;\n" |
| + "typealias integer { size = 23; align = 1; signed = true; base = p; } := int23_t;\n" |
| + "typealias integer { size = 22; align = 1; signed = true; base = 16; } := int22_t;\n" |
| + "typealias integer { size = 21; align = 1; signed = true; base = oct; } := int21_t;\n" |
| + "typealias integer { size = 20; align = 1; signed = true; base = b; } := int20_t;\n" |
| + "typealias integer { size = 19; align = 1; signed = true; base = octal; } := int19_t;\n" |
| + "typealias integer { size = 18; align = 1; signed = true; base = o; } := int18_t;\n" |
| + "typealias integer { size = 17; align = 1; signed = true; base = binary; } := int17_t;\n" |
| + "\n" |
| + "trace {\n" |
| + " major = 1;\n" |
| + " minor = 8;\n" |
| + " uuid = \"b04d391b-e736-44c1-8d89-4bb438857f8d\";\n" |
| + " byte_order = le;\n" |
| + " packet.header := struct {\n" |
| + " uint32_t magic;\n" |
| + " uint8_t uuid[16];\n" |
| + " uint32_t stream_id;\n" + " };\n" + "};\n" + "\n"; |
| private static final String environmentMD = "env {\n" |
| + " hostname = \"DemoSystem\";\n" |
| + " vpid = 1337;\n" |
| + " procname = \"demo\";\n" |
| + " domain = \"autogenerated\";\n" |
| + " tracer_name = \"tmf\";\n" |
| + " tracer_major = 2;\n" |
| + " tracer_minor = 0x01;\n" |
| + " tracer_patchlevel = 0;\n" |
| + "};\n" + "\n"; |
| private static final String clockMD = "clock {\n" + " name = monotonic;\n" |
| + " uuid = \"cbf9f42e-9be7-4798-a96f-11db556e2ebb\";\n" |
| + " description = \"Monotonic Clock\";\n" |
| + " freq = 1000000000; /* Frequency, in Hz */\n" |
| + " /* clock value offset from Epoch is: offset * (1/freq) */\n" |
| + " offset = 1350310657466295832;\n" + "};\n" |
| + "\n"; |
| |
| private static final String ctfStart = |
| "typealias integer {\n" |
| + " size = 27; align = 1; signed = false;\n" |
| + " map = clock.monotonic.value;\n" |
| + "} := uint27_clock_monotonic_t;\n" |
| + "\n" |
| + "typealias integer {\n" |
| + " size = 32; align = 8; signed = false;\n" |
| + " map = clock.monotonic.value;\n" |
| + "} := uint32_clock_monotonic_t;\n" |
| + "\n" |
| + "typealias integer {\n" |
| + " size = 64; align = 8; signed = false;\n" |
| + " map = clock.monotonic.value;\n" |
| + "} := uint64_clock_monotonic_t;\n" |
| + "\n"; |
| |
| private static final String ctfHeaders = |
| "struct packet_context {\n" |
| + " uint64_clock_monotonic_t timestamp_begin;\n" |
| + " uint64_clock_monotonic_t timestamp_end;\n" |
| + " uint64_t content_size;\n" |
| + " uint64_t packet_size;\n" |
| + " unsigned long events_discarded;\n" |
| + " uint32_t cpu_id;\n" |
| + "};\n" |
| + "\n" |
| + "struct event_header_compact {\n" |
| + " enum : uint5_t { compact = 0 ... 30, extended = 31 } id;\n" |
| + " variant <id> {\n" |
| + " struct {\n" |
| + " uint27_clock_monotonic_t timestamp;\n" |
| + " } compact;\n" |
| + " struct {\n" |
| + " uint32_t id;\n" |
| + " uint64_clock_monotonic_t timestamp;\n" |
| + " } extended;\n" |
| + " } v;\n" |
| + "} align(8);\n" |
| + "\n" |
| + "struct event_header_large {\n" |
| + " enum : uint16_t { compact = 0 ... 65534, extended = 65535 } id;\n" |
| + " variant <id> {\n" + " struct {\n" |
| + " uint32_clock_monotonic_t timestamp;\n" |
| + " } compact;\n" + " struct {\n" |
| + " uint32_t id;\n" |
| + " uint64_clock_monotonic_t timestamp;\n" |
| + " } extended;\n" + " } v;\n" + "} align(8);\n" + "\n"; |
| |
| private static final String ctfBody = "stream {\n" |
| + " id = 0;\n" |
| + " event.header := struct event_header_compact;\n" |
| + " packet.context := struct packet_context;\n" |
| + " event.context := struct {\n" |
| + " integer { size = 64; align = 8; signed = 0; encoding = none; base = 16; } _ip;\n" |
| + " };\n" |
| + "};\n" |
| + "\n" |
| + "event {\n" |
| + " name = \"ust_tests_demo3:done\";\n" |
| + " id = 0;\n" |
| + " stream_id = 0;\n" |
| + " loglevel = 4;\n" |
| + " fields := struct {\n" |
| + " integer { size = 32; align = 8; signed = 1; encoding = none; base = 10; } _value;\n" |
| + " };\n" |
| + "};\n" |
| + "\n" |
| + "event {\n" |
| + " name = \"ust_tests_demo:starting\";\n" |
| + " id = 1;\n" |
| + " stream_id = 0;\n" |
| + " loglevel = 2;\n" |
| + " model.emf.uri = \"http://example.com/path_to_model?q=ust_tests_demo:starting\";\n" |
| + " fields := struct {\n" |
| + " integer { size = 32; align = 8; signed = 1; encoding = none; base = 10; } _value;\n" |
| + " };\n" |
| + "};\n" |
| + "\n" |
| + "event {\n" |
| + " name = \"ust_tests_demo:done\";\n" |
| + " id = 2;\n" |
| + " stream_id = 0;\n" |
| + " loglevel = 2;\n" |
| + " model.emf.uri = \"http://example.com/path_to_model?q=ust_tests_demo:done\";\n" |
| + " fields := struct {\n" |
| + " integer { size = 32; align = 8; signed = 1; encoding = none; base = 10; } _value;\n" |
| + " };\n" |
| + "};\n" |
| + "\n" |
| + "event {\n" |
| + " name = \"ust_tests_demo2:loop\";\n" |
| + " id = 3;\n" |
| + " stream_id = 0;\n" |
| + " loglevel = 4;\n" |
| + " fields := struct {\n" |
| + " integer { size = 32; align = 8; signed = 1; encoding = none; base = 10; } _intfield;\n" |
| + " integer { size = 32; align = 8; signed = 1; encoding = none; base = 16; } _intfield2;\n" |
| + " integer { size = 64; align = 8; signed = 1; encoding = none; base = 10; } _longfield;\n" |
| + " integer { size = 32; align = 8; signed = 1; encoding = none; base = 10; byte_order = be; } _netintfield;\n" |
| + " integer { size = 32; align = 8; signed = 1; encoding = none; base = 16; byte_order = be; } _netintfieldhex;\n" |
| + " integer { size = 64; align = 8; signed = 1; encoding = none; base = 10; } _arrfield1[3];\n" |
| + " integer { size = 8; align = 8; signed = 1; encoding = UTF8; base = 10; } _arrfield2[10];\n" |
| + " integer { size = 64; align = 8; signed = 0; encoding = none; base = 10; } __seqfield1_length;\n" |
| + " integer { size = 8; align = 8; signed = 1; encoding = none; base = 10; } _seqfield1[ __seqfield1_length ];\n" |
| + " integer { size = 64; align = 8; signed = 0; encoding = none; base = 10; } __seqfield2_length;\n" |
| + " integer { size = 8; align = 8; signed = 1; encoding = UTF8; base = 10; } _seqfield2[ __seqfield2_length ];\n" |
| + " string _stringfield;\n" |
| + " floating_point { exp_dig = 8; mant_dig = 24; align = 8; } _floatfield;\n" |
| + " floating_point { exp_dig = 11; mant_dig = 53; align = 8; } _doublefield;\n" |
| + " };\n" |
| + "};\n" |
| + "\n"; |
| |
| private static final String enumMd = |
| "typealias integer { size = 32; align = 8; signed = false; } := int;\n" |
| + "typealias enum { ONE = 0, a,b,c=10, d} := useless_enum;\n" |
| + "struct useless{ \n" |
| + " enum : uint8_t { A=0, \"B\",} enum3;\n" |
| + " useless_enum enum2;" |
| + " enum { C, D, E } enum4;\n" |
| + " uint16_t val;\n" |
| + "} ;\n" |
| + "\n" |
| + "event {\n" |
| + " name = \"enumEvent\";\n" |
| + " id = 6;\n" |
| + " stream_id = 0;\n" |
| + " loglevel = 5;\n" |
| + " fields := struct{\n" |
| + " uint16_t _some_field;\n" |
| // + " useless junk;\n" |
| // + " bad_enum a;\n" |
| + " enum {A, B, C = 3 , } _other_enum;\n" |
| + " };\n" |
| + "};\n" |
| + "\n"; |
| |
| private final static String contextMD = |
| "event {\n" + |
| " name = \"someOtherEvent\";\n" + |
| " id = 5;\n" + |
| " stream_id = 0;\n" + |
| " loglevel = 5;\n" + |
| " context := struct{\n" + |
| " uint16_t _someContext;\n" + |
| " };\n" + |
| " fields := struct{\n" + |
| " uint16_t _somefield;\n" + |
| " };\n" + |
| "};\n " + |
| "\n"; |
| |
| private static final String callsiteMD = |
| "callsite {\n" |
| + " name = \"ust_tests_demo2:loop\";\n" |
| + " func = \"main\";\n" + " ip = 0x400a29;\n" |
| + " file = \"demo.c\";\n" + " line = 59;\n" + "};\n" + "\n" |
| + "callsite {\n" + " name = \"ust_tests_demo3:done\";\n" |
| + " func = \"main\";\n" + " ip = 0x400a6c;\n" |
| + " file = \"demo.c\";\n" + " line = 62;\n" + "};\n" + "\n" |
| + "callsite {\n" + " name = \"ust_tests_demo:done\";\n" |
| + " func = \"main\";\n" + " ip = 0x400aaf;\n" |
| + " file = \"demo.c\";\n" + " line = 61;\n" + "};\n" + "\n" |
| + "callsite {\n" + " name = \"ust_tests_demo:starting\";\n" |
| + " func = \"main\";\n" + " ip = 0x400af2;\n" |
| + " file = \"demo.c\";\n" + " line = 55;\n" + "};\n"; |
| |
| private static final String simpleTSDL = metadataDecs + ctfStart + ctfHeaders |
| + ctfBody; |
| private static final String enumTSDL = metadataDecs + ctfStart + ctfHeaders |
| + ctfBody + enumMd; |
| private static final String clockTSDL = metadataDecs + clockMD + ctfStart |
| + ctfHeaders + ctfBody; |
| private static final String envTSDL = metadataDecs + environmentMD + ctfStart |
| + ctfHeaders + ctfBody; |
| private static final String contextTSDL = metadataDecs + environmentMD + ctfStart |
| + ctfHeaders + ctfBody + contextMD; |
| private static final String callsiteTSDL = metadataDecs + ctfStart + ctfHeaders |
| + ctfBody + callsiteMD; |
| private static final String allDressedTSDL = metadataDecs + environmentMD + clockMD |
| + ctfStart + ctfHeaders + ctfBody + enumMd + callsiteMD; |
| |
| static final String tempTraceDir = CtfCoreTestPlugin.getTemporaryDirPath() |
| + File.separator + "tempTrace"; |
| |
| private static final int DATA_SIZE = 4096; |
| |
| private static final int HEADER_SIZE = 68; |
| |
| private static final int PACKET_SIZE = DATA_SIZE + HEADER_SIZE + 512; |
| |
| private CTFTrace trace; |
| |
| private static class Event { |
| private static final int EVENT_SIZE = 16; |
| private int eventId; |
| private int eventTimestamp; |
| private int eventContent; |
| |
| public Event(int id, int content) { |
| eventId = id; |
| eventTimestamp = 0; |
| eventContent = content; |
| } |
| |
| public void setEventTimestamp(int eventTimestamp) { |
| this.eventTimestamp = eventTimestamp; |
| } |
| |
| public void setEventContent(int eventContent) { |
| this.eventContent = eventContent; |
| } |
| |
| public void writeEvent(ByteBuffer data) { |
| // Id and Timestamp |
| int timeId = eventTimestamp << 5; |
| timeId |= eventId & 0x1f; |
| data.putInt(timeId); |
| |
| // Context |
| long ip = 0x0000facedecafe00L + ((data.position() / |
| getSize()) & 0x0F); |
| data.putLong(ip); |
| |
| // Content |
| data.putInt(eventContent); |
| |
| } |
| |
| public int getSize() { |
| return EVENT_SIZE; |
| } |
| |
| } |
| |
| private static void deltree(File f) { |
| for (File elem : f.listFiles()) { |
| if (elem.isDirectory()) { |
| deltree(elem); |
| } |
| elem.delete(); |
| } |
| f.delete(); |
| } |
| |
| private static void createDummyTrace(String metadata) { |
| File dir = new File(tempTraceDir); |
| if (dir.exists()) { |
| deltree(dir); |
| } |
| dir.mkdirs(); |
| |
| File metadataFile = new File(tempTraceDir + "/metadata"); |
| try (FileWriter fw = new FileWriter(metadataFile);) { |
| fw.write(metadata); |
| } catch (IOException e) { |
| e.printStackTrace(); |
| } |
| |
| byte magicLE[] = { (byte) 0xC1, (byte) 0x1F, (byte) 0xFC, |
| (byte) 0xC1 }; |
| byte uuid[] = { (byte) 0xb0, 0x4d, 0x39, 0x1b, (byte) 0xe7, |
| 0x36, 0x44, (byte) 0xc1, (byte) 0x8d, (byte) 0x89, 0x4b, |
| (byte) 0xb4, 0x38, (byte) 0x85, 0x7f, (byte) 0x8d }; |
| |
| Event ev = new Event(2, 2); |
| |
| final int nbEvents = (DATA_SIZE / ev.getSize()) - 1; |
| final int contentSize = (nbEvents * ev.getSize() + |
| HEADER_SIZE) * 8; |
| |
| ByteBuffer data = ByteBuffer.allocate(PACKET_SIZE); |
| data.order(ByteOrder.LITTLE_ENDIAN); |
| data.clear(); |
| |
| // packet header |
| // magic number 4 |
| data.put(magicLE); |
| // uuid 16 |
| data.put(uuid); |
| // stream ID 4 |
| data.putInt(0); |
| |
| // packet context |
| // timestamp_begin 8 |
| data.putLong(0xa500); |
| |
| // timestamp_end 8 |
| data.putLong(nbEvents * 0x10000 + 0xa5a6); |
| |
| // content_size 8 |
| data.putLong(contentSize); |
| |
| // packet_size 8 |
| data.putLong(PACKET_SIZE * 8); |
| |
| // events_discarded 8 |
| data.putLong(0); |
| |
| // cpu_id 4 |
| data.putInt(0); |
| |
| // fill me |
| for (int i = 0; i < nbEvents; i++) { |
| ev.setEventTimestamp(i * 0x10000 + 0xa5a5); |
| ev.setEventContent(i); |
| ev.writeEvent(data); |
| } |
| |
| // The byteBuffer needs to be flipped in file writing mode |
| data.flip(); |
| |
| File dummyFile = new File(tempTraceDir + "/dummyChan"); |
| try (FileOutputStream fos = new FileOutputStream(dummyFile);) { |
| fos.getChannel().write(data); |
| } catch (IOException e) { |
| e.printStackTrace(); |
| } |
| |
| } |
| |
| /** |
| * Simple test (only the minimum) |
| * |
| * @throws CTFException |
| * something wrong happened |
| */ |
| @Test |
| public void TSDLSimpleTest() throws CTFException { |
| createDummyTrace(simpleTSDL); |
| trace = new CTFTrace(tempTraceDir); |
| assertNotNull(trace); |
| } |
| |
| /** |
| * Test with environment variables |
| * |
| * @throws CTFException |
| * something wrong happened |
| */ |
| @Test |
| public void TSDLEnvironmentTest() throws CTFException { |
| createDummyTrace(envTSDL); |
| trace = new CTFTrace(tempTraceDir); |
| assertNotNull(trace); |
| } |
| |
| /** |
| * Test with Clocks |
| * |
| * @throws CTFException |
| * something wrong happened |
| */ |
| @Test |
| public void TSDLEnumTest() throws CTFException { |
| createDummyTrace(enumTSDL); |
| trace = new CTFTrace(tempTraceDir); |
| assertNotNull(trace); |
| } |
| |
| /** |
| * Test with Clocks |
| * |
| * @throws CTFException |
| * something wrong happened |
| */ |
| @Test |
| public void TSDLClockTest() throws CTFException { |
| createDummyTrace(clockTSDL); |
| trace = new CTFTrace(tempTraceDir); |
| assertNotNull(trace); |
| } |
| |
| /** |
| * Test with Contexts |
| * |
| * @throws CTFException |
| * something wrong happened |
| */ |
| @Test |
| public void TSDLContextTest() throws CTFException { |
| createDummyTrace(contextTSDL); |
| trace = new CTFTrace(tempTraceDir); |
| assertNotNull(trace); |
| } |
| |
| /** |
| * Test with Callsites |
| * |
| * @throws CTFException |
| * something wrong happened |
| */ |
| @Test |
| public void TSDLCallsiteTest() throws CTFException { |
| createDummyTrace(callsiteTSDL); |
| trace = new CTFTrace(tempTraceDir); |
| assertNotNull(trace); |
| } |
| |
| /** |
| * Test everything |
| * |
| * @throws CTFException |
| * something wrong happened |
| */ |
| @Test |
| public void TSDLAllTest() throws CTFException { |
| createDummyTrace(allDressedTSDL); |
| trace = new CTFTrace(tempTraceDir); |
| assertNotNull(trace); |
| |
| final List<IEventDeclaration> eventDeclarations = new ArrayList<>(trace.getEventDeclarations(0L)); |
| final EventDeclaration eventDeclaration = (EventDeclaration) eventDeclarations.get(2); |
| assertEquals("http://example.com/path_to_model?q=ust_tests_demo:done", |
| eventDeclaration.getCustomAttribute("model.emf.uri")); |
| } |
| |
| } |