XCTraceTableOfContentsHandler.java
/*
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package org.openjdk.jmh.profile;
import org.xml.sax.Attributes;
import java.time.Instant;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.*;
import java.util.stream.Collectors;
/**
* Extracts information about xctrace result tables from trace file's table of contents.
*
* The most interesting part of the ToC is a set of tables containing information about table's format and
* various recoding parameters, such as names of collected PMCs.
*/
class XCTraceTableOfContentsHandler extends XCTraceTableHandler {
private static final DateTimeFormatter TOC_DATE_FORMAT = DateTimeFormatter.ISO_OFFSET_DATE_TIME;
private final List<XCTraceTableDesc> supportedTables = new ArrayList<>();
private long recordStartMs;
private static List<String> parseEvents(Attributes attributes) {
String events = attributes.getValue(XCTraceTableHandler.PMC_EVENTS);
return Arrays.stream(events.split(" ")).map(e -> {
if (!e.startsWith("\"") && !e.endsWith("\"")) return e;
if (e.startsWith("\"") && e.endsWith("\"")) return e.substring(1, e.length() - 1);
throw new IllegalStateException("Can't parse pmc-events: " + events);
}).filter(e -> !e.isEmpty())
.collect(Collectors.toList());
}
public List<XCTraceTableDesc> getSupportedTables() {
return Collections.unmodifiableList(supportedTables);
}
public long getRecordStartMs() {
return recordStartMs;
}
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) {
setNeedParseCharacters(qName.equals(XCTraceTableHandler.START_DATE));
if (!qName.equals(XCTraceTableHandler.TABLE)) {
return;
}
String schema = Objects.requireNonNull(attributes.getValue(XCTraceTableHandler.SCHEMA), "Schema not found");
if (schema.equals(ProfilingTableType.CPU_PROFILE.tableName)) {
supportedTables.add(XCTraceTableDesc.CPU_PROFILE);
} else if (schema.equals(ProfilingTableType.TIME_PROFILE.tableName)) {
supportedTables.add(XCTraceTableDesc.TIME_PROFILE);
} else if (schema.equals(ProfilingTableType.COUNTERS_PROFILE.tableName)) {
parseCountersProfile(attributes);
}
}
@Override
public void endElement(String uri, String localName, String qName) {
if (!qName.equals(XCTraceTableHandler.START_DATE)) {
return;
}
try {
recordStartMs = Instant.from(TOC_DATE_FORMAT.parse(getCharacters())).toEpochMilli();
} catch (DateTimeParseException e) {
throw new IllegalStateException(e);
} finally {
setNeedParseCharacters(false);
}
}
private void parseCountersProfile(Attributes attributes) {
String trigger = Objects.requireNonNull(attributes.getValue(XCTraceTableHandler.TRIGGER));
TriggerType triggerType = TriggerType.valueOf(trigger.toUpperCase());
if (triggerType == TriggerType.PMI) {
parsePmiSampleTable(attributes);
} else if (triggerType == TriggerType.TIME) {
parseTimeSampleTable(attributes);
} else {
throw new IllegalStateException("Unsupported trigger type: " + triggerType);
}
}
private void parsePmiSampleTable(Attributes attributes) {
String pmiEvent = Objects.requireNonNull(attributes.getValue(XCTraceTableHandler.PMI_EVENT),
"Trigger event not found");
if (pmiEvent.startsWith("\"") && pmiEvent.endsWith("\"")) {
pmiEvent = pmiEvent.substring(1, pmiEvent.length() - 1);
}
long threshold = Long.parseLong(Objects.requireNonNull(attributes.getValue(XCTraceTableHandler.PMI_THRESHOLD),
"Trigger threshold not found"));
XCTraceTableDesc table = new XCTraceTableDesc(ProfilingTableType.COUNTERS_PROFILE, TriggerType.PMI,
pmiEvent, threshold, parseEvents(attributes));
supportedTables.add(table);
}
private void parseTimeSampleTable(Attributes attributes) {
long threshold = Long.parseLong(Objects.requireNonNull(attributes.getValue(XCTraceTableHandler.SAMPLE_RATE),
"Trigger threshold not found"));
XCTraceTableDesc table = new XCTraceTableDesc(ProfilingTableType.COUNTERS_PROFILE, TriggerType.TIME,
XCTraceTableProfileHandler.XCTraceSample.TIME_SAMPLE_TRIGGER_NAME, threshold, parseEvents(attributes));
supportedTables.add(table);
}
}