/*******************************************************************************
* Copyright (c) 2017 Rogue Wave Software Inc. and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Rogue Wave Software Inc. - initial implementation
*******************************************************************************/
package org.eclipse.php.profile.core.engine;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.eclipse.php.internal.core.util.XMLWriter;
import org.eclipse.php.internal.debug.core.zend.debugger.CodeCoverageData;
import org.eclipse.php.profile.core.PHPProfileCorePlugin;
import org.eclipse.php.profile.core.data.ProfilerCallTrace;
import org.eclipse.php.profile.core.data.ProfilerCallTraceLayer;
import org.eclipse.php.profile.core.data.ProfilerData;
import org.eclipse.php.profile.core.data.ProfilerFileData;
import org.eclipse.php.profile.core.data.ProfilerFunctionData;
import org.eclipse.php.profile.core.data.ProfilerGlobalData;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
/**
* Profiler data serialization utility class.
*/
public class ProfilerDataSerializationUtil {
private XMLWriter fXML;
/**
* Used for serialization
*
* @param XMLWriter
* xml
*/
private ProfilerDataSerializationUtil(XMLWriter xml) {
fXML = xml;
}
/**
* Used for deserialization
*/
private ProfilerDataSerializationUtil() {
}
private static String pack(byte[] arr) {
StringBuilder buf = new StringBuilder();
buf.append(arr.length).append(":"); //$NON-NLS-1$
for (int i = 0; i < arr.length; ++i) {
buf.append(arr[i]);
if (i < arr.length - 1) {
buf.append(","); //$NON-NLS-1$
}
}
return buf.toString();
}
private static byte[] unpackByte(String buf) {
byte[] arr = null;
int idx = buf.indexOf(':');
if (idx != -1) {
int arr_len = Integer.parseInt(buf.substring(0, idx));
String[] bytes = buf.substring(idx + 1).split(","); //$NON-NLS-1$
if (bytes.length == arr_len) {
arr = new byte[arr_len];
for (int i = 0; i < arr_len; ++i) {
arr[i] = Byte.parseByte(bytes[i]);
}
}
}
return arr;
}
// =========================================== Serialization methods
// ========================================================== //
public static void serialize(ProfilerDB[] profilerDBs, OutputStream out) {
XMLWriter xml = null;
try {
xml = new XMLWriter(out);
ProfilerDataSerializationUtil su = new ProfilerDataSerializationUtil(
xml);
xml.startTag("profilerDB", null); //$NON-NLS-1$
for (int i = 0; i < profilerDBs.length; ++i) {
HashMap<String, String> parameters = new HashMap<String, String>();
parameters
.put(
"date", Long.toString(profilerDBs[i].getProfileDate().getTime())); //$NON-NLS-1$
xml.startTag("profileSession", parameters); //$NON-NLS-1$
su.serialize(profilerDBs[i].getProfilerData());
xml.endTag("profileSession"); //$NON-NLS-1$
}
xml.endTag("profilerDB"); //$NON-NLS-1$
} catch (UnsupportedEncodingException e) {
PHPProfileCorePlugin.log(e);
} finally {
if (xml != null) {
xml.flush();
xml.close();
}
}
}
private void serialize(ProfilerData data) {
if (data == null) {
return;
}
serialize(data.getGlobalData());
serialize(data.getCallTrace());
ProfilerFileData[] fileData = data.getFiles();
for (int i = 0; i < fileData.length; ++i) {
serialize(fileData[i]);
}
}
private void serialize(ProfilerCallTrace callTrace) {
if (callTrace == null) {
return;
}
HashMap<String, String> parameters = new HashMap<String, String>();
parameters.put("layers", Integer.toString(callTrace.getLayersCount())); //$NON-NLS-1$
fXML.startTag("callTrace", parameters); //$NON-NLS-1$
ProfilerCallTraceLayer[] layers = callTrace.getLayers();
for (int i = 0; i < layers.length; ++i) {
serialize(layers[i]);
}
fXML.endTag("callTrace"); //$NON-NLS-1$
}
private void serialize(ProfilerCallTraceLayer layer) {
if (layer == null) {
return;
}
HashMap<String, String> parameters = new HashMap<String, String>();
parameters.put("type", Integer.toString(layer.getType())); //$NON-NLS-1$
parameters.put("line", Integer.toString(layer.getLineNumber())); //$NON-NLS-1$
parameters.put("id", Integer.toString(layer.getCalledID())); //$NON-NLS-1$
parameters.put(
"timestampS", Integer.toString(layer.getTimestampSeconds())); //$NON-NLS-1$
parameters
.put(
"timestampM", Integer.toString(layer.getTimestampMicroseconds())); //$NON-NLS-1$
parameters.put(
"durationS", Integer.toString(layer.getDurationSeconds())); //$NON-NLS-1$
parameters.put(
"durationM", Integer.toString(layer.getDurationMicroeconds())); //$NON-NLS-1$
fXML.startTag("callTraceLayer", parameters); //$NON-NLS-1$
fXML.endTag("callTraceLayer"); //$NON-NLS-1$
}
private void serialize(ProfilerGlobalData data) {
if (data == null) {
return;
}
HashMap<String, String> parameters = new HashMap<String, String>();
parameters.put("uri", data.getURI()); //$NON-NLS-1$
parameters.put("originalURL", data.getOriginalURL()); //$NON-NLS-1$
parameters.put("query", data.getQuery()); //$NON-NLS-1$
parameters.put("options", data.getOptions()); //$NON-NLS-1$
parameters.put("path", data.getPath()); //$NON-NLS-1$
parameters.put("timeS", Integer.toString(data.getTimeSeconds())); //$NON-NLS-1$
parameters.put("timeM", Integer.toString(data.getTimeMicroSeconds())); //$NON-NLS-1$
parameters.put("dataSize", Integer.toString(data.getDataSize())); //$NON-NLS-1$
parameters.put("files", Integer.toString(data.getFileCount())); //$NON-NLS-1$
fXML.startTag("globalData", parameters); //$NON-NLS-1$
String[] fileNames = data.getFileNames();
for (int i = 0; i < fileNames.length; ++i) {
fXML.printSimpleTag("file", fileNames[i]); //$NON-NLS-1$
}
fXML.endTag("globalData"); //$NON-NLS-1$
}
private void serialize(ProfilerFileData data) {
if (data == null) {
return;
}
HashMap<String, String> parameters = new HashMap<String, String>();
parameters.put("name", data.getName()); //$NON-NLS-1$
parameters.put("local", data.getLocalName()); //$NON-NLS-1$
parameters.put("functions", Integer.toString(data.getFunctionsCount())); //$NON-NLS-1$
parameters.put("time", Double.toString(data.getTotalOwnTime())); //$NON-NLS-1$
fXML.startTag("fileData", parameters); //$NON-NLS-1$
ProfilerFunctionData[] functionData = data.getFunctions();
for (int i = 0; i < functionData.length; ++i) {
serialize(functionData[i]);
}
serialize(data.getCodeCoverageData());
fXML.endTag("fileData"); //$NON-NLS-1$
}
private void serialize(ProfilerFunctionData data) {
if (data == null) {
return;
}
HashMap<String, String> parameters = new HashMap<String, String>();
parameters.put("file", data.getAbsoluteFileName()); //$NON-NLS-1$
parameters.put("localFile", data.getLocalFileName()); //$NON-NLS-1$
parameters.put("name", data.toString()); //$NON-NLS-1$
parameters.put("line", Integer.toString(data.getLineNumber())); //$NON-NLS-1$
parameters.put("id", Integer.toString(data.getID())); //$NON-NLS-1$
parameters.put("ownS", Integer.toString(data.getOwnTimeSeconds())); //$NON-NLS-1$
parameters.put("ownM", Integer.toString(data.getOwnTimeMicroseconds())); //$NON-NLS-1$
parameters.put("totalS", Integer.toString(data.getTotalTimeSeconds())); //$NON-NLS-1$
parameters.put(
"totalM", Integer.toString(data.getTotalTimeMicroseconds())); //$NON-NLS-1$
parameters.put("calls", Integer.toString(data.getCallsCount())); //$NON-NLS-1$
fXML.startTag("functionData", parameters); //$NON-NLS-1$
fXML.endTag("functionData"); //$NON-NLS-1$
}
private void serialize(CodeCoverageData data) {
if (data == null) {
return;
}
HashMap<String, String> parameters = new HashMap<String, String>();
parameters.put("file", data.getFileName()); //$NON-NLS-1$
parameters.put("localFile", data.getLocalFileName()); //$NON-NLS-1$
parameters.put("linesNum", Integer.toString(data.getLinesNum())); //$NON-NLS-1$
parameters.put("phpLinesNum", Integer.toString(data.getPhpLinesNum())); //$NON-NLS-1$
parameters.put("coverageBitmask", pack(data.getCoverageBitmask())); //$NON-NLS-1$
parameters.put(
"significanceBitmask", pack(data.getSignificanceBitmask())); //$NON-NLS-1$
fXML.startTag("coverageData", parameters); //$NON-NLS-1$
fXML.endTag("coverageData"); //$NON-NLS-1$
}
// =========================================== Deserialization methods
// ========================================================== //
public static ProfilerDB[] deserialize(InputStream in) {
try {
DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory
.newInstance();
// docBuilderFactory.setValidating(true);
DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder();
Document doc = docBuilder.parse(in);
ArrayList<ProfilerDB> profilerDBs = new ArrayList<ProfilerDB>();
NodeList l = doc.getElementsByTagName("profileSession"); //$NON-NLS-1$
for (int i = 0; l.item(i) != null; ++i) {
if (l.item(i).getNodeType() == Node.ELEMENT_NODE) {
Element e = (Element) l.item(i);
Date profileDate = new Date(Long.parseLong(e
.getAttribute("date"))); //$NON-NLS-1$
ProfilerDataSerializationUtil su = new ProfilerDataSerializationUtil();
ProfilerDB db = new DefaultProfilerDB(su
.deserializeProfilerData(e), profileDate);
profilerDBs.add(db);
}
}
return profilerDBs.toArray(new ProfilerDB[profilerDBs.size()]);
} catch (Exception e) {
// ProfilePlugin.log(e);
}
return null;
}
private ProfilerData deserializeProfilerData(Element rootElement) {
Element e = (Element) rootElement
.getElementsByTagName("globalData").item(0); //$NON-NLS-1$
ProfilerGlobalData globalData = deserializeGlobalData(e);
e = (Element) rootElement.getElementsByTagName("callTrace").item(0); //$NON-NLS-1$
ProfilerCallTrace callTrace = deserializeCallTrace(e);
ArrayList<ProfilerFileData> fileData = new ArrayList<ProfilerFileData>();
NodeList l = rootElement.getElementsByTagName("fileData"); //$NON-NLS-1$
for (int i = 0; l.item(i) != null; ++i) {
fileData.add(deserializeFileData((Element) l.item(i), globalData));
}
return new ProfilerData(globalData, fileData, callTrace);
}
private ProfilerFileData deserializeFileData(Element rootElement,
ProfilerGlobalData globalData) {
String fileName = rootElement.getAttribute("name"); //$NON-NLS-1$
String localFileName = rootElement.getAttribute("local"); //$NON-NLS-1$
int functionsNum = Integer.parseInt(rootElement
.getAttribute("functions")); //$NON-NLS-1$
double totalOwnTime = Double.parseDouble(rootElement
.getAttribute("time")); //$NON-NLS-1$
ArrayList<ProfilerFunctionData> functionData = new ArrayList<ProfilerFunctionData>();
NodeList l = rootElement.getElementsByTagName("functionData"); //$NON-NLS-1$
for (int i = 0; l.item(i) != null; ++i) {
functionData.add(deserializeFunctionData((Element) l.item(i)));
}
Element e = (Element) rootElement
.getElementsByTagName("coverageData").item(0); //$NON-NLS-1$
CodeCoverageData codeCoverage = deserializeCoverageData(e, globalData);
ProfilerFileData data = new ProfilerFileData(fileName, localFileName,
functionsNum, totalOwnTime, functionData);
data.setCodeCoverageData(codeCoverage);
return data;
}
private ProfilerFunctionData deserializeFunctionData(Element rootElement) {
String fileName = rootElement.getAttribute("file"); //$NON-NLS-1$
String localFileName = rootElement.getAttribute("localFile"); //$NON-NLS-1$
String functionName = rootElement.getAttribute("name"); //$NON-NLS-1$
int lineNumber = Integer.parseInt(rootElement.getAttribute("line")); //$NON-NLS-1$
int id = Integer.parseInt(rootElement.getAttribute("id")); //$NON-NLS-1$
int ownTimeSeconds = Integer.parseInt(rootElement.getAttribute("ownS")); //$NON-NLS-1$
int ownTimeMicroSeconds = Integer.parseInt(rootElement
.getAttribute("ownM")); //$NON-NLS-1$
int totalTimeSeconds = Integer.parseInt(rootElement
.getAttribute("totalS")); //$NON-NLS-1$
int totalTimeMicroSeconds = Integer.parseInt(rootElement
.getAttribute("totalM")); //$NON-NLS-1$
int callsCount = Integer.parseInt(rootElement.getAttribute("calls")); //$NON-NLS-1$
ProfilerFunctionData functionData = new ProfilerFunctionData(fileName,
functionName, lineNumber, id, ownTimeSeconds,
ownTimeMicroSeconds, totalTimeSeconds, totalTimeMicroSeconds,
callsCount);
functionData.setLocalFileName(localFileName);
return functionData;
}
private ProfilerCallTrace deserializeCallTrace(Element rootElement) {
int layersNum = Integer.parseInt(rootElement.getAttribute("layers")); //$NON-NLS-1$
NodeList l = rootElement.getElementsByTagName("callTraceLayer"); //$NON-NLS-1$
ArrayList<ProfilerCallTraceLayer> layers = new ArrayList<ProfilerCallTraceLayer>();
for (int i = 0; l.item(i) != null; ++i) {
layers.add(deserializeCallTraceLayer((Element) l.item(i)));
}
ProfilerCallTrace callTrace = new ProfilerCallTrace(layers);
callTrace.setLayersCount(layersNum);
return callTrace;
}
private ProfilerCallTraceLayer deserializeCallTraceLayer(Element rootElement) {
int type = Integer.parseInt(rootElement.getAttribute("type")); //$NON-NLS-1$
int lineNumber = Integer.parseInt(rootElement.getAttribute("line")); //$NON-NLS-1$
int id = Integer.parseInt(rootElement.getAttribute("id")); //$NON-NLS-1$
int timestampSeconds = Integer.parseInt(rootElement
.getAttribute("timestampS")); //$NON-NLS-1$
int timestampMicroSeconds = Integer.parseInt(rootElement
.getAttribute("timestampM")); //$NON-NLS-1$
int durationSeconds = Integer.parseInt(rootElement
.getAttribute("durationS")); //$NON-NLS-1$
int durationMicroSeconds = Integer.parseInt(rootElement
.getAttribute("durationM")); //$NON-NLS-1$
return new ProfilerCallTraceLayer(type, lineNumber, id,
timestampSeconds, timestampMicroSeconds, durationSeconds,
durationMicroSeconds);
}
private ProfilerGlobalData deserializeGlobalData(Element rootElement) {
String uri = rootElement.getAttribute("uri"); //$NON-NLS-1$
String originalURL = rootElement.getAttribute("originalURL"); //$NON-NLS-1$
String query = rootElement.getAttribute("query"); //$NON-NLS-1$
String options = rootElement.getAttribute("options"); //$NON-NLS-1$
String path = rootElement.getAttribute("path"); //$NON-NLS-1$
int timeSeconds = Integer.parseInt(rootElement.getAttribute("timeS")); //$NON-NLS-1$
int timeMicroseconds = Integer.parseInt(rootElement
.getAttribute("timeM")); //$NON-NLS-1$
int dataSize = Integer.parseInt(rootElement.getAttribute("dataSize")); //$NON-NLS-1$
int filesNumber = Integer.parseInt(rootElement.getAttribute("files")); //$NON-NLS-1$
ArrayList<String> files = new ArrayList<String>();
NodeList l = rootElement.getElementsByTagName("file"); //$NON-NLS-1$
for (int i = 0; l.item(i) != null; ++i) {
files.add(l.item(i).getFirstChild().getNodeValue());
}
return new ProfilerGlobalData(uri, originalURL, query, options, path,
timeSeconds, timeMicroseconds, dataSize, filesNumber, files);
}
private CodeCoverageData deserializeCoverageData(Element rootElement,
ProfilerGlobalData globalData) {
if (rootElement == null) {
return null;
}
String fileName = rootElement.getAttribute("file"); //$NON-NLS-1$
String localFileName = rootElement.getAttribute("localFile"); //$NON-NLS-1$
int linesNum = Integer.parseInt(rootElement.getAttribute("linesNum")); //$NON-NLS-1$
int phpLinesNum = Integer.parseInt(rootElement
.getAttribute("phpLinesNum")); //$NON-NLS-1$
byte[] coverageBitmask = unpackByte(rootElement
.getAttribute("coverageBitmask")); //$NON-NLS-1$
byte[] significanceBitmask = unpackByte(rootElement
.getAttribute("significanceBitmask")); //$NON-NLS-1$
CodeCoverageData data = new CodeCoverageData(fileName, linesNum,
coverageBitmask);
data.setURL(globalData.getOriginalURL());
data.setLocalFileName(localFileName);
data.setPhpLinesNum(phpLinesNum);
data.setSignificanceBitmask(significanceBitmask);
return data;
}
}