Skip to content

Commit

Permalink
Add data tree end point which can be used for statistics data providers
Browse files Browse the repository at this point in the history
It contains also unit tests for that.

[Added] data tree end point for e.g. statistics data providers

Change-Id: I5e0268cc72c47a49d1effaee653a7ff952d6df07
Signed-off-by: Bernd Hufmann <bernd.hufmann@ericsson.com>
Reviewed-on: https://git.eclipse.org/r/c/tracecompass.incubator/org.eclipse.tracecompass.incubator/+/199994
Tested-by: Trace Compass Bot <tracecompass-bot@eclipse.org>
Tested-by: Patrick Tasse <patrick.tasse@gmail.com>
Reviewed-by: Patrick Tasse <patrick.tasse@gmail.com>
  • Loading branch information
bhufmann committed Mar 2, 2023
1 parent ffbbc08 commit f08051f
Show file tree
Hide file tree
Showing 4 changed files with 153 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

import javax.ws.rs.ProcessingException;
import javax.ws.rs.client.Entity;
Expand All @@ -39,6 +40,8 @@
import org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests.stubs.ColumnHeaderEntryStub;
import org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests.stubs.DataProviderDescriptorStub;
import org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests.stubs.EntryHeaderStub;
import org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests.stubs.EntryModelStub;
import org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests.stubs.EntryStub;
import org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests.stubs.ExperimentModelStub;
import org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests.stubs.LineModelStub;
import org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests.stubs.TableColumnsOutputResponseStub;
Expand All @@ -52,6 +55,7 @@
import org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests.stubs.TimeGraphModelStub;
import org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests.stubs.TimeGraphRowStub;
import org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests.stubs.TimeGraphStateStub;
import org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests.stubs.TreeOutputResponseStub;
import org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests.stubs.XyEntryModelStub;
import org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests.stubs.XyEntryStub;
import org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests.stubs.XyModelStub;
Expand All @@ -77,6 +81,7 @@
public class DataProviderServiceTest extends RestServerTest {
private static final int MAX_ITER = 40;
private static final String CALL_STACK_DATAPROVIDER_ID = "org.eclipse.tracecompass.internal.analysis.profiling.callstack.provider.CallStackDataProvider";
private static final String STATISTICS_DATAPROVIDER_ID = "org.eclipse.tracecompass.analysis.timing.core.segmentstore.SegmentStoreStatisticsDataProvider:org.eclipse.linuxtools.lttng2.ust.analysis.callstack";
private static final String XY_DATAPROVIDER_ID = "org.eclipse.tracecompass.analysis.os.linux.core.cpuusage.CpuUsageDataProvider";
private static final String XY_HISTOGRAM_DATAPROVIDER_ID = "org.eclipse.tracecompass.internal.tmf.core.histogram.HistogramDataProvider";
private static final String EVENTS_TABLE_DATAPROVIDER_ID = "org.eclipse.tracecompass.internal.provisional.tmf.core.model.events.TmfEventTableDataProvider";
Expand All @@ -87,6 +92,7 @@ public class DataProviderServiceTest extends RestServerTest {
private static final String REQUESTED_COLUMN_IDS_KEY = "requested_table_column_ids";
private static final String REQUESTED_TABLE_INDEX_KEY = "requested_table_index";
private static final String REQUESTED_TABLE_COUNT_KEY = "requested_table_count";
private static final String IS_FILTERED_KEY = "isFiltered";
private static final String ELEMENT_TYPE = "elementType";
private static final String STATE = "state";
private static final String ANNOTATION = "annotation";
Expand All @@ -103,6 +109,10 @@ public class DataProviderServiceTest extends RestServerTest {

private static final List<EntryHeaderStub> EXPECTED_XY_TREE_HEADERS = ImmutableList.of(new EntryHeaderStub("Process", ""), new EntryHeaderStub("TID", ""), new EntryHeaderStub("%", ""), new EntryHeaderStub("Time", ""));

private static List<String> STATISTICS_TREE_HEADERS = ImmutableList.of("Label", "Minimum", "Maximum", "Average", "Std Dev", "Count", "Total");
private static List<String> SAMPLE_TOTAL_STATS_LABELS = ImmutableList.of("ust", "1 ns", "5.979 s", "10.845 ms", "196.299 ms", "1948", "21.127 s");
private static List<String> SAMPLE_SELECTION_STATS_LABELS = ImmutableList.of("Selection", "49.665 µs", "5.979 s", "11.388 ms", "201.201 ms", "1854", "21.113 s");

/**
* Test getting the data provider descriptors
*/
Expand Down Expand Up @@ -282,6 +292,57 @@ public void testHistogramDataProvider() throws InterruptedException {
}
}

/**
* Ensure that a data tree data provider exists and returns correct data.
* It does not test the data itself, simply that the serialized fields are
* the expected ones.
*
* @throws InterruptedException
* Exception thrown while waiting to execute again
*/
@Test
public void testDataTreeDataProvider() throws InterruptedException {
long start = 1450193697034689597L;
long end = 1450193745774189602L;
try {
ExperimentModelStub exp = assertPostExperiment(CONTEXT_SWITCHES_UST_STUB.getName(), CONTEXT_SWITCHES_UST_STUB);

// Test getting the time graph tree
WebTarget dataTree = getDataTreeEndpoint(exp.getUUID().toString(), STATISTICS_DATAPROVIDER_ID);

Map<String, Object> parameters = new HashMap<>();
parameters.put(REQUESTED_TIMERANGE_KEY, ImmutableMap.of(START, start, END, end));
EntryModelStub model = getDataTreeEntryModel(dataTree, parameters);
List<EntryStub> totalEntries = model.getEntries();

List<String> sampleLabels = totalEntries.get(0).getLabels();
for(String sample : SAMPLE_TOTAL_STATS_LABELS) {
assertTrue(sampleLabels.contains(sample));
}

// Query selection time range
end = end - 100000000L;
parameters.put(REQUESTED_TIMERANGE_KEY, ImmutableMap.of(START, start, END, end));
parameters.put(IS_FILTERED_KEY, true);
model = getDataTreeEntryModel(dataTree, parameters);
List<EntryStub> selectionRangeEntries = model.getEntries();
assertFalse(selectionRangeEntries.isEmpty());
// the result model contains total and selection statistics
assertTrue(selectionRangeEntries.size() > totalEntries.size());

sampleLabels = selectionRangeEntries.get(totalEntries.size()).getLabels();
for(String sample : SAMPLE_SELECTION_STATS_LABELS) {
assertTrue(sampleLabels.contains(sample));
}

} catch (ProcessingException e) {
// The failure from this exception alone is not helpful. Use the
// suppressed exception's message be the failure message for more
// help debugging failed tests.
fail(e.getCause().getMessage());
}
}

/**
* Ensure that a time graph data provider exists and returns correct data.
* It does not test the data itself, simply that the serialized fields are
Expand Down Expand Up @@ -589,4 +650,36 @@ private static void verifyEntry(TimeGraphEntryStub entry) {
assertNull(entry.getMetadata());
}
}

private static EntryModelStub getDataTreeEntryModel(WebTarget dataTree, Map<String, Object> parameters) throws InterruptedException {
TreeOutputResponseStub responseModel;
Response treeResponse = dataTree.request().post(Entity.json(new QueryParameters(parameters, Collections.emptyList())));
assertEquals("There should be a positive response for the data provider", 200, treeResponse.getStatus());
responseModel = treeResponse.readEntity(TreeOutputResponseStub.class);
assertNotNull(responseModel);
treeResponse.close();

// Make sure the analysis ran enough and we have a model
int iteration = 0;
while ((responseModel.isRunning() || responseModel.getModel() == null) && iteration < MAX_ITER) {
Thread.sleep(100);
treeResponse = dataTree.request().post(Entity.json(new QueryParameters(parameters, Collections.emptyList())));
assertEquals("There should be a positive response for the data provider", 200, treeResponse.getStatus());
responseModel = treeResponse.readEntity(TreeOutputResponseStub.class);
assertNotNull(responseModel);
iteration++;
treeResponse.close();
}

EntryModelStub model = responseModel.getModel();
assertNotNull("The model is null, maybe the analysis did not run long enough?" + responseModel, model);
List<EntryStub> totalEntries = model.getEntries();
assertFalse(totalEntries.isEmpty());

List<String> headerLabels = model.getHeaders().stream().map(EntryHeaderStub::getName).collect(Collectors.toList());
for(String header : STATISTICS_TREE_HEADERS) {
assertTrue(headerLabels.contains(header));
}
return model;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,11 @@ public abstract class RestServerTest {
*/
public static final String TREE_PATH = "tree";

/**
* Data Tree path segment
*/
public static final String DATATREE_PATH = "data";

/**
* Time Graph path segment
*/
Expand Down Expand Up @@ -279,6 +284,25 @@ public static WebTarget getTableLinesEndpoint(String expUUID, String dataProvide
.path(TABLE_LINE_PATH);
}


/**
* Get the {@link WebTarget} for the data-tree tree endpoint.
*
* @param expUUID
* Experiment UUID
* @param dataProviderId
* Data provider ID
* @return The time graph tree endpoint
*/
public static WebTarget getDataTreeEndpoint(String expUUID, String dataProviderId) {
return getApplicationEndpoint().path(EXPERIMENTS)
.path(expUUID)
.path(OUTPUTS_PATH)
.path(DATATREE_PATH)
.path(dataProviderId)
.path(TREE_PATH);
}

/**
* Get the {@link WebTarget} for the time graph tree endpoint.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import static org.eclipse.tracecompass.incubator.internal.trace.server.jersey.rest.core.services.EndpointConstants.DIRECTION;
import static org.eclipse.tracecompass.incubator.internal.trace.server.jersey.rest.core.services.EndpointConstants.DIRECTION_COUNT;
import static org.eclipse.tracecompass.incubator.internal.trace.server.jersey.rest.core.services.EndpointConstants.DIRECTION_EX;
import static org.eclipse.tracecompass.incubator.internal.trace.server.jersey.rest.core.services.EndpointConstants.DT;
import static org.eclipse.tracecompass.incubator.internal.trace.server.jersey.rest.core.services.EndpointConstants.ELEMENT;
import static org.eclipse.tracecompass.incubator.internal.trace.server.jersey.rest.core.services.EndpointConstants.ELEMENT_EX;
import static org.eclipse.tracecompass.incubator.internal.trace.server.jersey.rest.core.services.EndpointConstants.EMAIL;
Expand Down Expand Up @@ -278,6 +279,40 @@ public Response getProvider(
return Response.status(Status.NOT_FOUND).build();
}

/**
* Query the provider for the data tree entries.
*
* @param expUUID
* desired experiment UUID
* @param outputId
* Output ID for the data provider to query
* @param queryParameters
* Parameters to fetch a data tree as described by
* {@link QueryParameters}
* @return an {@link GenericView} with the results
*/
@POST
@Path("/data/{outputId}/tree")
@Tag(name = DT)
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
@Operation(summary = "API to get the data tree", description = TREE_ENTRIES, responses = {
@ApiResponse(responseCode = "200", description = "Returns a list of data tree entries. " +
CONSISTENT_PARENT, content = @Content(schema = @Schema(implementation = XYTreeResponse.class))),
@ApiResponse(responseCode = "400", description = INVALID_PARAMETERS, content = @Content(schema = @Schema(implementation = String.class))),
@ApiResponse(responseCode = "404", description = PROVIDER_NOT_FOUND, content = @Content(schema = @Schema(implementation = String.class))),
@ApiResponse(responseCode = "405", description = NO_PROVIDER, content = @Content(schema = @Schema(implementation = String.class)))
})
public Response getDataTree(
@Parameter(description = EXP_UUID) @PathParam("expUUID") UUID expUUID,
@Parameter(description = OUTPUT_ID) @PathParam("outputId") String outputId,
@RequestBody(description = "Query parameters to fetch the data tree entries. " + TIMERANGE_TREE, content = {
@Content(examples = @ExampleObject("{\"parameters\":{" + TIMERANGE_EX_TREE +
"}}"), schema = @Schema(implementation = TreeQueryParameters.class))
}, required = true) QueryParameters queryParameters) {
return getTree(expUUID, outputId, queryParameters);
}

/**
* Query the provider for the XY tree
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ public final class EndpointConstants {
*/
static final String ANN = "Annotations"; //$NON-NLS-1$
static final String DIA = "Diagnostic"; //$NON-NLS-1$
static final String DT = "Data Tree"; //$NON-NLS-1$
static final String EXP = "Experiments"; //$NON-NLS-1$
static final String STY = "Styles"; //$NON-NLS-1$
static final String TGR = "TimeGraph"; //$NON-NLS-1$
Expand Down

0 comments on commit f08051f

Please sign in to comment.