blob: 41bac9eda1f83016f8c7619288ef0aa12c3337ff [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2009, 2010 Nokia 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:
* Nokia - Initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.debug.edc.internal.snapshot;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerException;
import org.eclipse.cdt.debug.core.sourcelookup.MappingSourceContainer;
import org.eclipse.cdt.debug.edc.internal.EDCDebugger;
import org.eclipse.cdt.debug.edc.internal.PathUtils;
import org.eclipse.cdt.debug.edc.internal.ZipFileUtils;
import org.eclipse.cdt.debug.edc.internal.services.dsf.RunControl;
import org.eclipse.cdt.debug.edc.launch.EDCLaunch;
import org.eclipse.cdt.debug.edc.services.Stack;
import org.eclipse.cdt.debug.edc.services.Stack.StackFrameDMC;
import org.eclipse.cdt.debug.edc.snapshot.IAlbum;
import org.eclipse.cdt.debug.internal.core.sourcelookup.MapEntrySourceContainer;
import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor;
import org.eclipse.cdt.dsf.concurrent.DsfRunnable;
import org.eclipse.cdt.dsf.concurrent.Query;
import org.eclipse.cdt.dsf.debug.service.IProcesses.IThreadDMContext;
import org.eclipse.cdt.dsf.debug.service.IStack.IFrameDMContext;
import org.eclipse.cdt.dsf.service.DsfServicesTracker;
import org.eclipse.cdt.dsf.service.DsfSession;
import org.eclipse.cdt.dsf.service.DsfSession.SessionEndedListener;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.FileLocator;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.PlatformObject;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.SubMonitor;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.core.runtime.preferences.IEclipsePreferences;
import org.eclipse.debug.core.DebugPlugin;
import org.eclipse.debug.core.sourcelookup.ISourceContainer;
import org.eclipse.debug.core.sourcelookup.ISourceLookupDirector;
import org.eclipse.debug.core.sourcelookup.containers.DirectorySourceContainer;
import org.eclipse.debug.internal.core.LaunchManager;
import org.osgi.framework.Bundle;
import org.osgi.service.prefs.BackingStoreException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
/**
* The Album class represents a series of snapshots that record moments in a
* debug session. An Album manages the collection of snapshots, common resources
* such as source files, persistence, and association with debug sessions.
*
* An Album is usually created during a debug session, saved at the conclusion
* of the session, and reopened by a launch delegate for a new snapshot debug
* session.
*
* When an Album is saved it's data and resources are archived in a snapshot
* file in a default location. When reopened the contents are expanded into a
* temporary directory and used to recreate the debug session.
*/
@SuppressWarnings("restriction")
public class Album extends PlatformObject implements IAlbum {
// XML element names
public static final String SNAPSHOT = "snapshot";
private static final String ALBUM = "album";
private static final String LAUNCH = "launch";
private static final String RESOURCES = "resources";
private static final String FILE = "file";
private static final String INFO = "info";
public static final String METADATA = "snapshotMetaData";
public static final String SNAPSHOT_LIST = "snapshots";
private static final String ALBUM_DATA = "album.xml";
private static final String ALBUM_VERSION = "100";
private static String[] DSA_FILE_EXTENSIONS = new String[] {"dsa"};
// Preferences
public static final String PREF_CREATION_CONTROL = "creation_control";
public static final String CREATE_MANUAL = "manual";
public static final String CREATE_WHEN_STOPPED = "suspend";
public static final String CREATE_AT_BEAKPOINTS = "breakpoints";
public static final String PREF_VARIABLE_CAPTURE_DEPTH = "variable_capture_depth";
public static final String PLAY_SNAPSHOT_DELAY_TIME = "play_snapshot_delay_time";
private static final String CAMERA_CLICK_WAV = "/sounds/camera_click.wav";
private Document document;
private Element albumRootElement;
private final List<Snapshot> snapshotList = new ArrayList<Snapshot>();
private String sessionID = "";
private String recordingSessionID = "";
private IPath albumRootDirectory;
private boolean launchConfigSaved;
private String launchType;
private HashMap<String, Object> launchProperties;
private String launchName = "";
private String name;
private boolean loaded;
private boolean metaDataLoaded;
private final Set<IPath> files = new HashSet<IPath>();
private int currentSnapshotIndex;
private IPath location;
private boolean resourceListSaved;
private boolean metadataSaved;
private boolean albumInfoSaved;
private String displayName;
private boolean playingSnapshots;
/**
* Listener for state changes on albums
*/
protected static List<ISnapshotAlbumEventListener> listeners = Collections.synchronizedList(new ArrayList<ISnapshotAlbumEventListener>());
private static Map<String, Album> albumsBySessionID = Collections.synchronizedMap(new HashMap<String, Album>());
private static Map<String, Album> albumsRecordingBySessionID = Collections.synchronizedMap(new HashMap<String, Album>());
private static Map<IPath, Album> albumsByLocation = Collections.synchronizedMap(new HashMap<IPath, Album>());
private static Map<String, Integer> launchNames = Collections.synchronizedMap(new HashMap<String, Integer>());
private static boolean sessionEndedListenerAdded;
private static SessionEndedListener sessionEndedListener = new SessionEndedListener() {
public void sessionEnded(DsfSession session) {
Album album = albumsRecordingBySessionID.get(session.getId());
if (album == null)
album = albumsBySessionID.get(session.getId());
if (album != null && session.getId().equals(album.getRecordingSessionID())) {
album.saveResources(new NullProgressMonitor());
album.setRecordingSessionID("");
}
synchronized (albumsRecordingBySessionID) {
albumsRecordingBySessionID.remove(session.getId());
}
synchronized (albumsBySessionID) {
albumsBySessionID.remove(session.getId());
}
if (album != null) {
for (ISnapshotAlbumEventListener l : new ArrayList<ISnapshotAlbumEventListener>(listeners)) {
l.snapshotSessionEnded(album, session);
}
}
}
};
public interface IAlbumArchiveEntry {
public String createEntryName(File file);
}
public Album() {
super();
try {
setDocument(DebugPlugin.newDocument());
if (!sessionEndedListenerAdded)
DsfSession.addSessionEndedListener(sessionEndedListener);
sessionEndedListenerAdded = true;
} catch (CoreException e) {
EDCDebugger.getMessageLogger().logError(null, e);
}
}
/* (non-Javadoc)
* @see org.eclipse.cdt.debug.edc.internal.snapshot.IAlbum#getName()
*/
public String getName() {
if (name == null) {
name = getDefaultAlbumName();
}
return name;
}
public void setName(String name) {
this.name = name;
}
public void setDisplayName(String displayName) {
this.displayName = displayName;
}
/* (non-Javadoc)
* @see org.eclipse.cdt.debug.edc.internal.snapshot.IAlbum#getDisplayName()
*/
public String getDisplayName() {
if (displayName == null || displayName.length() == 0) {
displayName = getName();
}
return displayName;
}
/* (non-Javadoc)
* @see org.eclipse.cdt.debug.edc.internal.snapshot.IAlbum#getSessionID()
*/
public String getSessionID() {
sessionID = "";
if (albumsBySessionID != null) {
for (Map.Entry<String, Album> entry : albumsBySessionID.entrySet()){
if (entry.getValue().location != null && entry.getValue().location.equals(getLocation())){
sessionID = entry.getKey();
}
}
}
return sessionID;
}
/* (non-Javadoc)
* @see org.eclipse.cdt.debug.edc.internal.snapshot.IAlbum#getRecordingSessionID()
*/
public String getRecordingSessionID() {
return recordingSessionID;
}
public void setRecordingSessionID(String sessionID) {
this.recordingSessionID = sessionID;
if (sessionID.length() > 0)
albumsRecordingBySessionID.put(sessionID, this);
}
public void setSessionID(String sessionID) {
this.sessionID = sessionID;
if (sessionID.length() > 0)
albumsBySessionID.put(sessionID, this);
}
/**
* Is the album currently open for recording
* @param sessionId
* @return true if the album is currently being recording by an active debug session
*/
public static boolean isSnapshotSession(String sessionId) {
EDCLaunch launch = EDCLaunch.getLaunchForSession(sessionId);
return launch != null && launch.isSnapshotLaunch();
}
/* (non-Javadoc)
* @see org.eclipse.cdt.debug.edc.internal.snapshot.IAlbum#createSnapshot(org.eclipse.cdt.dsf.service.DsfSession, org.eclipse.cdt.debug.edc.internal.services.dsf.Stack.StackFrameDMC, org.eclipse.core.runtime.IProgressMonitor)
*/
public Snapshot createSnapshot(DsfSession session, StackFrameDMC stackFrame, IProgressMonitor monitor) throws Exception {
SubMonitor progress = SubMonitor.convert(monitor, "Creating Snapshot", 10000);
configureAlbum();
progress.worked(100);
if (getLocation() == null || !getLocation().toFile().exists()){
createEmptyAlbum();
}
Snapshot snapshot = new Snapshot(this, session, stackFrame);
snapshot.writeSnapshotData(progress.newChild(7900));
snapshotList.add(snapshot);
saveAlbum(progress.newChild(1000));
monitor.done();
return snapshot;
}
private void configureAlbum() {
saveAlbumInfo();
saveLaunchConfiguration();
}
private void saveAlbumInfo() {
if (!albumInfoSaved) {
Element infoElement = document.createElement(INFO);
infoElement.setAttribute("version", ALBUM_VERSION);
Calendar calendar = Calendar.getInstance();
infoElement.setAttribute("month", Integer.toString(calendar.get(Calendar.MONTH)));
infoElement.setAttribute("day", Integer.toString(calendar.get(Calendar.DATE)));
infoElement.setAttribute("year", Integer.toString(calendar.get(Calendar.YEAR)));
infoElement.setAttribute("hour", Integer.toString(calendar.get(Calendar.HOUR)));
infoElement.setAttribute("minute", Integer.toString(calendar.get(Calendar.MINUTE)));
infoElement.setAttribute("second", Integer.toString(calendar.get(Calendar.SECOND)));
Properties systemProps = System.getProperties();
Map<String, Object> infoProps = new HashMap<String, Object>();
Set<Object> systemKeys = systemProps.keySet();
for (Object sysKey : systemKeys) {
if (sysKey instanceof String)
infoProps.put((String) sysKey, systemProps.get(sysKey));
}
Element propsElement = SnapshotUtils.makeXMLFromProperties(document, infoProps);
infoElement.appendChild(propsElement);
getAlbumRootElement().appendChild(infoElement);
albumInfoSaved = true;
}
}
private void saveResourceList() {
if (!resourceListSaved) {
Element resourcesElement = document.createElement(RESOURCES);
for (IPath filePath : files) {
Element fileElement = document.createElement(FILE);
fileElement.setAttribute("path", filePath.toOSString());
resourcesElement.appendChild(fileElement);
}
getAlbumRootElement().appendChild(resourcesElement);
resourceListSaved = true;
}
}
private void saveSnapshotMetadata() {
if (!metadataSaved || isRecording()) {
if (metadataSaved){
// If metatdata is saved, it must be a live debug session so
// we need to add a new snapshot to the snapshot list
NodeList snapMetaDataNode = document.getElementsByTagName(METADATA);
assert snapMetaDataNode.item(0) != null;
document.getDocumentElement().removeChild(snapMetaDataNode.item(0));
}
Element metadataElement = document.createElement(METADATA);
Element albumElement = document.createElement(ALBUM);
albumElement.setAttribute("albumName", this.getDisplayName());
metadataElement.appendChild(albumElement);
Element snapshotsElement = document.createElement(SNAPSHOT_LIST);
metadataElement.appendChild(snapshotsElement);
for (Snapshot snap : snapshotList) {
Element snapshotMetadataElement = document.createElement(SNAPSHOT);
if (snap.getSnapshotDisplayName().length() == 0) {
snapshotMetadataElement.setAttribute("displayName", snap.getSnapshotFileName());
} else {
snapshotMetadataElement.setAttribute("displayName", snap.getSnapshotDisplayName());
}
if (snap.getCreationDate() != null) {
snapshotMetadataElement.setAttribute("date", snap.getCreationDate().toString());
} else {
snapshotMetadataElement.setAttribute("date", "unknown");
}
snapshotMetadataElement.setAttribute("description", snap.getSnapshotDescription());
snapshotMetadataElement.setAttribute("fileName", snap.getSnapshotFileName());
snapshotMetadataElement.setAttribute("referenceLocationSourceFile", snap.getReferenceLocationSourceFile());
snapshotMetadataElement.setAttribute("referenceLocationLineNumber", String.valueOf(snap.getReferenceLocationLineNumber()));
snapshotsElement.appendChild(snapshotMetadataElement);
}
getAlbumRootElement().appendChild(metadataElement);
metadataSaved = true;
}
}
@SuppressWarnings("unchecked")
private void saveLaunchConfiguration() {
if (!launchConfigSaved) {
EDCLaunch launch = EDCLaunch.getLaunchForSession(getRecordingSessionID());
try {
Map<String, Object> map = launch.getLaunchConfiguration().getAttributes();
Element launchElement = document.createElement(LAUNCH);
launchType = launch.getLaunchConfiguration().getType().getIdentifier();
launchName = launch.getLaunchConfiguration().getName();
Integer count = launchNames.get(launchName);
if (count == null) {
launchNames.put(launchName, new Integer(0));
}
else {
count = new Integer(count.intValue() + 1);
launchNames.put(launchName, count);
launchName += " (" + count.toString() + ")";
}
launchElement.setAttribute("type", launchType);
launchElement.setAttribute("name", launchName);
Element propsElement = SnapshotUtils.makeXMLFromProperties(document, map);
launchElement.appendChild(propsElement);
getAlbumRootElement().appendChild(launchElement);
} catch (CoreException e) {
EDCDebugger.getMessageLogger().logError(null, e);
}
launchConfigSaved = true;
}
}
private static void addZipEntry(ZipOutputStream zipOut, IAlbumArchiveEntry entry, File file)
throws FileNotFoundException, IOException {
if (file.exists()) {
if (file.isDirectory()) {
for (File child : file.listFiles()) {
addZipEntry(zipOut, entry, child);
}
} else {
// Add ZIP entry to output stream.m
String path = ""; //$NON-NLS-1$
if (entry != null) {
path = entry.createEntryName(file);
} else {
path = file.getName();
}
zipOut.putNextEntry(new ZipEntry(path));
// Create a buffer for reading the files
byte[] buf = new byte[1024];
// Transfer bytes from the file to the ZIP file
// and compress the files
FileInputStream in = new FileInputStream(file);
int len;
while ((len = in.read(buf)) > 0) {
zipOut.write(buf, 0, len);
}
// Complete the entry
zipOut.closeEntry();
in.close();
}
}
}
/**
* Create and write a full snapshot album from scratch
*/
private void saveAlbum(IProgressMonitor monitor) {
IPath zipPath = getLocation();
ZipOutputStream zipOut = null;
try {
SubMonitor progress = SubMonitor.convert(monitor, 2000 + (snapshotList.size() * 1000));
progress.subTask("Saving album data");
zipOut = new ZipOutputStream(new FileOutputStream(zipPath.toFile()));
zipOut.putNextEntry(new ZipEntry(ALBUM_DATA));
saveResourceList();
progress.worked(1000);
saveSnapshotMetadata();
progress.worked(1000);
String xml = LaunchManager.serializeDocument(document);
zipOut.write(xml.getBytes("UTF8")); //$NON-NLS-1$
zipOut.closeEntry();
for (Snapshot snap : snapshotList) {
zipOut.putNextEntry(new ZipEntry(snap.getSnapshotFileName()));
snap.saveSnapshot(zipOut);
progress.worked(1000);
}
} catch (Exception e) {
EDCDebugger.getMessageLogger().logError(null, e);
} finally {
try {
if (zipOut != null) {
zipOut.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* Create and write a full snapshot album from scratch
*/
private void saveResources(IProgressMonitor monitor) {
IPath zipPath = getLocation();
ZipOutputStream zipOut = null;
try {
// TODO: Here's we're just rewriting the entire album again
// Need to just add the resources alone using proper utils
zipOut = new ZipOutputStream(new FileOutputStream(zipPath.toFile()));
for (IPath path : files) {
IAlbumArchiveEntry entry = new IAlbumArchiveEntry() {
public String createEntryName(File file) {
StringBuffer entryPath = new StringBuffer();
entryPath.append("Resources/");
IPath filepath = new Path(file.getAbsolutePath());
String deviceName = filepath.getDevice();
if (deviceName != null) {
// Remove the : from the end
entryPath.append(deviceName.substring(0, deviceName.length() - 1));
entryPath.append("/");
}
String[] segments = filepath.segments();
int numSegments = segments.length - 1;
for (int i = 0; i < numSegments; i++) {
entryPath.append(segments[i]);
entryPath.append("/");
}
entryPath.append(file.getName());
return entryPath.toString();
}
};
addZipEntry(zipOut, entry, path.toFile());
if (monitor != null) {
monitor.worked(1);
}
}
zipOut.putNextEntry(new ZipEntry(ALBUM_DATA));
saveResourceList();
saveSnapshotMetadata();
String xml = LaunchManager.serializeDocument(document);
zipOut.write(xml.getBytes("UTF8")); //$NON-NLS-1$
zipOut.closeEntry();
for (Snapshot snap : snapshotList) {
zipOut.putNextEntry(new ZipEntry(snap.getSnapshotFileName()));
snap.saveSnapshot(zipOut);
}
} catch (Exception e) {
EDCDebugger.getMessageLogger().logError(null, e);
} finally {
try {
if (zipOut != null) {
zipOut.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
private String getDefaultAlbumName() {
return getLaunchName();
}
public void saveAlbum(IPath path) throws TransformerException, IOException {
String xml = LaunchManager.serializeDocument(document);
File file = path.toFile();
if (!file.exists()) {
file.createNewFile();
}
FileOutputStream stream = new FileOutputStream(file);
stream.write(xml.getBytes("UTF8")); //$NON-NLS-1$
stream.close();
}
/* (non-Javadoc)
* @see org.eclipse.cdt.debug.edc.internal.snapshot.IAlbum#openSnapshot(int)
*/
public void openSnapshot(final int index) {
final DsfSession session = DsfSession.getSession(sessionID);
DsfRunnable openIt = new DsfRunnable() {
public void run() {
currentSnapshotIndex = index;
try {
loadAlbum(false);
} catch (Exception e) {
EDCDebugger.getMessageLogger().logError(null, e);
}
if (session != null && snapshotList.size() > index) {
Snapshot snapshot = snapshotList.get(index);
snapshot.open(session);
// Fire the event
for (ISnapshotAlbumEventListener l : new ArrayList<ISnapshotAlbumEventListener>(listeners)) {
l.snapshotOpened(snapshot);
}
}
}
};
if (session != null && session.getExecutor() != null)
{
if (session.getExecutor().isInExecutorThread())
openIt.run();
else
session.getExecutor().execute(openIt);
}
}
/* (non-Javadoc)
* @see org.eclipse.cdt.debug.edc.internal.snapshot.IAlbum#getCurrentSnapshotIndex()
*/
public int getCurrentSnapshotIndex() {
return currentSnapshotIndex;
}
/* (non-Javadoc)
* @see org.eclipse.cdt.debug.edc.internal.snapshot.IAlbum#openNextSnapshot()
*/
public void openNextSnapshot() throws Exception {
int nextIndex = currentSnapshotIndex + 1;
if (nextIndex >= snapshotList.size())
nextIndex = 0;
openSnapshot(nextIndex);
}
/* (non-Javadoc)
* @see org.eclipse.cdt.debug.edc.internal.snapshot.IAlbum#openPreviousSnapshot()
*/
public void openPreviousSnapshot() throws Exception {
int previousIndex = currentSnapshotIndex - 1;
if (previousIndex < 0)
previousIndex = snapshotList.size() - 1;
openSnapshot(previousIndex);
}
public void loadAlbum(boolean force) throws ParserConfigurationException, SAXException, IOException {
if (force)
loaded = false;
if (!loaded) {
File albumFile = location.toFile();
setName(albumFile.getName());
if (!isRecording()){
// not creating the snapshot, so must be snapshot play back
try {
ZipFileUtils.unzipFiles(albumFile, getAlbumRootDirectory().toOSString(), new NullProgressMonitor());
} catch (Exception e) {
EDCDebugger.getMessageLogger().logError(null, e);
}
}
BufferedInputStream stream = ZipFileUtils.openFile(albumFile, ALBUM_DATA, DSA_FILE_EXTENSIONS);
DocumentBuilder parser = DocumentBuilderFactory.newInstance().newDocumentBuilder();
parser.setErrorHandler(new DefaultHandler());
InputSource inputSource = new InputSource(stream);
inputSource.setSystemId(albumFile.toURI().toString()); // avoid NPE inside XML parser
setDocument(parser.parse(inputSource));
loadAlbumInfo();
loadLaunchConfiguration();
loadResourceList();
try {
loadSnapshotMetadata();
} catch (Exception e) {
e.printStackTrace();
} finally {
loaded = true;
ZipFileUtils.unmount();
}
}
}
/**
* A lightwieght parse to get basic album info and what snapshots are
* available.
*
* @throws ParserConfigurationException
* @throws SAXException
* @throws IOException
*/
public void loadAlbumMetada(boolean force) throws Exception {
if (force)
metaDataLoaded = false;
if (!metaDataLoaded) {
File albumFile = location.toFile();
setDisplayName(albumFile.getName());
BufferedInputStream stream = null;
try {
stream = ZipFileUtils.openFile(albumFile, ALBUM_DATA, DSA_FILE_EXTENSIONS);
DocumentBuilder parser = DocumentBuilderFactory.newInstance().newDocumentBuilder();
parser.setErrorHandler(new DefaultHandler());
setDocument(parser.parse(new InputSource(stream)));
loadSnapshotMetadata();
loadLaunchConfiguration(); // need to load launch config in case we need to delete it
} catch (Exception e) {
EDCDebugger.getMessageLogger().logError("Failed to load album: " + getName(), e);
} finally {
metaDataLoaded = true;
ZipFileUtils.unmount();
}
}
}
private void loadAlbumInfo() {
document.getElementsByTagName(INFO).item(0);
}
private void setDocument(Document newDoc)
{
document = newDoc;
albumRootElement = null;
}
private Element getAlbumRootElement()
{
if (albumRootElement == null)
{
NodeList albumRootElements = document.getElementsByTagName(ALBUM);
if (albumRootElements.getLength() == 0)
{
albumRootElement = document.createElement(ALBUM);
document.appendChild(albumRootElement);
}
else
{
albumRootElement = (Element) albumRootElements.item(0);
}
}
return albumRootElement;
}
private void loadResourceList() {
NodeList resources = document.getElementsByTagName(RESOURCES);
NodeList elementFiles = ((Element) resources.item(0)).getElementsByTagName(FILE);
int numFiles = elementFiles.getLength();
for (int i = 0; i < numFiles; i++) {
Element fileElement = (Element) elementFiles.item(i);
String elementPath = fileElement.getAttribute("path");
files.add(PathUtils.createPath(elementPath)); // for cross-created snapshot
}
}
private void loadSnapshotMetadata() throws Exception {
snapshotList.clear();
NodeList snapMetaDataNode = document.getElementsByTagName(METADATA);
if (snapMetaDataNode.getLength() == 0) {
throw new Exception("Invalid or corrupted Album : " + getName());
}
NodeList albumNameElement = ((Element) snapMetaDataNode.item(0)).getElementsByTagName(ALBUM);
Element albumElement = (Element) albumNameElement.item(0);
String albumDisplayName = albumElement.getAttribute("albumName");
setDisplayName(albumDisplayName);
NodeList elementSnapshots = ((Element) snapMetaDataNode.item(0)).getElementsByTagName(SNAPSHOT);
int numSnapshots = elementSnapshots.getLength();
for (int i = 0; i < numSnapshots; i++) {
Element snapshotElement = (Element) elementSnapshots.item(i);
String elementDescription = snapshotElement.getAttribute("description");
String elementDate = snapshotElement.getAttribute("date");
String elementDispalyName = snapshotElement.getAttribute("displayName");
String elementFileName = snapshotElement.getAttribute("fileName");
String referenceLocationSourceFile = snapshotElement.getAttribute("referenceLocationSourceFile");
String referenceLocationLineNumber = snapshotElement.getAttribute("referenceLocationLineNumber");
Snapshot s = new Snapshot(this);
s.setCreationDate(elementDate);
s.setSnapshotFileName(elementFileName);
s.setSnapshotDisplayName(elementDispalyName);
s.setSnapshotDescription(elementDescription);
if (referenceLocationLineNumber.length() > 0){
s.setReferenceLocationLineNumber(Long.parseLong(referenceLocationLineNumber));
}
s.setReferenceLocationSourceFile(referenceLocationSourceFile);
snapshotList.add(s);
}
}
private void loadLaunchConfiguration() {
NodeList launchElements = document.getElementsByTagName(LAUNCH);
Element launchElement = (Element) launchElements.item(0);
if (launchElement == null){
return;
}
launchType = launchElement.getAttribute("type");
launchName = launchElement.getAttribute("name");
Element propElement = (Element) launchElement.getElementsByTagName(SnapshotUtils.PROPERTIES).item(0);
launchProperties = new HashMap<String, Object>();
try {
SnapshotUtils.initializeFromXML(propElement, launchProperties);
} catch (Exception e) {
EDCDebugger.getMessageLogger().logError(null, e);
}
}
@SuppressWarnings("rawtypes")
@Override
public Object getAdapter(Class adapter) {
if (adapter.equals(Document.class))
return document;
return super.getAdapter(adapter);
}
/* (non-Javadoc)
* @see org.eclipse.cdt.debug.edc.internal.snapshot.IAlbum#getAlbumRootDirectory()
*/
public IPath getAlbumRootDirectory() {
if (albumRootDirectory == null) {
IPath path = EDCDebugger.getDefault().getStateLocation().append("SnapshotAlbums");
String locationName = location.lastSegment();
int extension = locationName.lastIndexOf(".");
if (extension > 0) {
locationName = locationName.substring(0, extension);
}
path = path.append(locationName);
File dir = path.toFile();
if (!dir.exists()) {
dir.mkdirs();
}
albumRootDirectory = path;
}
return albumRootDirectory;
}
/* (non-Javadoc)
* @see org.eclipse.cdt.debug.edc.internal.snapshot.IAlbum#getLaunchTypeID()
*/
public String getLaunchTypeID() {
return launchType;
}
/* (non-Javadoc)
* @see org.eclipse.cdt.debug.edc.internal.snapshot.IAlbum#getLaunchProperties()
*/
public HashMap<String, Object> getLaunchProperties() {
return launchProperties;
}
public void setLaunchProperties(HashMap<String, Object> launchProperties) {
this.launchProperties = launchProperties;
}
/* (non-Javadoc)
* @see org.eclipse.cdt.debug.edc.internal.snapshot.IAlbum#getLaunchName()
*/
public String getLaunchName() {
return launchName;
}
/* (non-Javadoc)
* @see org.eclipse.cdt.debug.edc.internal.snapshot.IAlbum#playSnapshots(org.eclipse.cdt.dsf.service.DsfSession)
*/
public void playSnapshots() {
if (!isPlayingSnapshots())
{
Job playSnapshotsJob = new Job("Play Snapshots for Album " + getDisplayName()){
@Override
protected IStatus run(IProgressMonitor monitor) {
try {
monitor.beginTask("Play Snapshots for Album " + getDisplayName(), IProgressMonitor.UNKNOWN);
while (isPlayingSnapshots() && !monitor.isCanceled())
{
Album.this.openNextSnapshot();
Thread.sleep(getPlaySnapshotInterval());
}
setPlayingSnapshots(false);
monitor.done();
} catch (Exception e) {
EDCDebugger.getMessageLogger().logError(null, e);
return new Status(Status.ERROR, EDCDebugger.PLUGIN_ID, null, e);
}
return Status.OK_STATUS;
}
};
setPlayingSnapshots(true);
playSnapshotsJob.schedule();
}
}
/* (non-Javadoc)
* @see org.eclipse.cdt.debug.edc.internal.snapshot.IAlbum#addFile(org.eclipse.core.runtime.IPath)
*/
public void addFile(IPath path) {
files.add(path);
}
public static Album getAlbumByLocation(IPath path) {
return albumsByLocation.get(path);
}
public static IAlbum getAlbumBySession(String sessionId) {
return albumsBySessionID.get(sessionId);
}
public static Album getRecordingForSession(String sessionId) {
return albumsRecordingBySessionID.get(sessionId);
}
public void setLocation(IPath albumPath) {
this.location = albumPath;
albumsByLocation.put(albumPath, this);
}
/* (non-Javadoc)
* @see org.eclipse.cdt.debug.edc.internal.snapshot.IAlbum#getLocation()
*/
public IPath getLocation() {
return location;
}
/* (non-Javadoc)
* @see org.eclipse.cdt.debug.edc.internal.snapshot.IAlbum#configureSourceLookupDirector(org.eclipse.cdt.debug.internal.core.sourcelookup.CSourceLookupDirector)
*/
public void configureSourceLookupDirector(ISourceLookupDirector director) {
MappingSourceContainer sourceContainer = new MappingSourceContainer(getName());
configureMappingSourceContainer(sourceContainer);
ArrayList<ISourceContainer> containers = new ArrayList<ISourceContainer>(Arrays.asList(director
.getSourceContainers()));
containers.add(sourceContainer);
DirectorySourceContainer directoryContainer = new DirectorySourceContainer(getResourcesDirectory(), true);
containers.add(directoryContainer);
director.setSourceContainers(containers.toArray(new ISourceContainer[containers.size()]));
}
protected IPath getResourcesDirectory()
{
return getAlbumRootDirectory().append("Resources");
}
/* (non-Javadoc)
* @see org.eclipse.cdt.debug.edc.internal.snapshot.IAlbum#configureMappingSourceContainer(org.eclipse.cdt.debug.core.sourcelookup.MappingSourceContainer)
*/
public void configureMappingSourceContainer(MappingSourceContainer mappingContainer) {
IPath albumRoot = getResourcesDirectory();
List<MapEntrySourceContainer> containers = new ArrayList<MapEntrySourceContainer>();
Set<String> devicesAlreadyAdded = new HashSet<String>();
String deviceName = null;
for (IPath iPath : files) {
deviceName = iPath.getDevice();
if (deviceName != null && !devicesAlreadyAdded.contains(deviceName))
{
String albumRootSuffix = deviceName;
if (albumRootSuffix.endsWith(":"))
albumRootSuffix = albumRootSuffix.substring(0, albumRootSuffix.length() - 1);
devicesAlreadyAdded.add(deviceName);
MapEntrySourceContainer newContainer = new MapEntrySourceContainer(PathUtils.createPath(deviceName), albumRoot.append(albumRootSuffix));
containers.add(newContainer);
}
}
mappingContainer.addMapEntries(containers.toArray(new MapEntrySourceContainer[containers.size()]));
}
public static void setVariableCaptureDepth(int newSetting) {
IEclipsePreferences scope = EDCDebugger.getPrefs(EDCDebugger.PLUGIN_ID);
scope.putInt(PREF_VARIABLE_CAPTURE_DEPTH, newSetting);
try {
scope.flush();
} catch (BackingStoreException e) {
EDCDebugger.getMessageLogger().logError(null, e);
}
}
public static int getVariableCaptureDepth() {
return Platform.getPreferencesService().getInt(EDCDebugger.PLUGIN_ID,
PREF_VARIABLE_CAPTURE_DEPTH, 5, null);
}
public static void setPlaySnapshotInterval(int delayInMilliseconds) {
IEclipsePreferences scope = EDCDebugger.getPrefs(EDCDebugger.PLUGIN_ID);
scope.putInt(PLAY_SNAPSHOT_DELAY_TIME, delayInMilliseconds);
try {
scope.flush();
} catch (BackingStoreException e) {
EDCDebugger.getMessageLogger().logError(null, e);
}
}
public static int getPlaySnapshotInterval() {
return Platform.getPreferencesService().getInt(EDCDebugger.PLUGIN_ID,
PLAY_SNAPSHOT_DELAY_TIME, 5000, null);
}
public static void setSnapshotCreationControl(String newSetting) {
IEclipsePreferences scope = EDCDebugger.getPrefs(EDCDebugger.PLUGIN_ID);
scope.put(PREF_CREATION_CONTROL, newSetting);
try {
scope.flush();
} catch (BackingStoreException e) {
EDCDebugger.getMessageLogger().logError(null, e);
}
}
public static String getSnapshotCreationControl() {
return Platform.getPreferencesService().getString(EDCDebugger.PLUGIN_ID,
PREF_CREATION_CONTROL, CREATE_MANUAL, null);
}
protected static void playSnapshotSound() {
Bundle bundle = Platform.getBundle(EDCDebugger.getUniqueIdentifier());
if (bundle == null)
return;
URL url = null;
try {
url = FileLocator.toFileURL(bundle.getEntry(CAMERA_CLICK_WAV));
} catch (IOException e) {
} catch (RuntimeException e){
}
finally {
if (url != null){
File f = new File(url.getFile());
SnapshotUtils.playSoundFile(f);
}
}
}
/* (non-Javadoc)
* @see org.eclipse.cdt.debug.edc.internal.snapshot.IAlbum#getSnapshots()
*/
public List<Snapshot> getSnapshots() {
if (snapshotList == null || snapshotList.size() == 0) {
try {
loadAlbumMetada(false);
} catch (Exception e) {
EDCDebugger.getMessageLogger().logError("Failed to load snapshots", e);
}
}
return snapshotList;
}
/* (non-Javadoc)
* @see org.eclipse.cdt.debug.edc.internal.snapshot.IAlbum#isLoaded()
*/
public boolean isLoaded() {
return loaded;
}
/* (non-Javadoc)
* @see org.eclipse.cdt.debug.edc.internal.snapshot.IAlbum#getIndexOfSnapshot(org.eclipse.cdt.debug.edc.internal.snapshot.Snapshot)
*/
public int getIndexOfSnapshot(Snapshot snap) {
return snapshotList.indexOf(snap);
}
public void setCurrentSnapshotIndex(int index) {
if (currentSnapshotIndex >= 0 && currentSnapshotIndex < snapshotList.size()) {
currentSnapshotIndex = index;
}
}
/**
* Update album.xml within the Album's .dsa file with new Snapshot data
*
* @param albumName
* - Name of album to display. Use null if value should not be
* updated.
* @param snap
* - Specific snapshot to update. Use null is snapshot should not
* be updated.
*/
public void updateSnapshotMetaData(String albumName, Snapshot snap) {
NodeList snapMetaDataNode = document.getElementsByTagName(METADATA);
// try to update album display name
if (albumName != null) {
NodeList albumNameNode = ((Element) snapMetaDataNode.item(0)).getElementsByTagName(ALBUM);
((Element) albumNameNode.item(0)).setAttribute("albumName", albumName);
}
// try to update snapshot data
if (snap != null) {
NodeList elementSnapshots = ((Element) snapMetaDataNode.item(0)).getElementsByTagName(SNAPSHOT);
int numSnapshots = elementSnapshots.getLength();
for (int i = 0; i < numSnapshots; i++) {
Element currentSnapshotNode = (Element) elementSnapshots.item(i);
String fileName = currentSnapshotNode.getAttribute("fileName");
if (fileName.equals(snap.getSnapshotFileName())) {
currentSnapshotNode.setAttribute("description", snap.getSnapshotDescription());
currentSnapshotNode.setAttribute("displayName", snap.getSnapshotDisplayName());
break;
}
}
}
saveAlbumData();
// refresh all data
try {
loadAlbumMetada(true);
} catch (Exception e) {
e.printStackTrace();
}
}
private void saveAlbumData() {
try {
File tempFile = File.createTempFile("album", ".xml");
File tempFile2 = new File(tempFile.getParent() + File.separator + ALBUM_DATA);
tempFile.delete();
if (!tempFile2.exists()) {
tempFile2.delete();
}
tempFile2.createNewFile();
saveAlbum(new Path(tempFile2.toString()));
File[] fileList = { tempFile2 };
ZipFileUtils.addFilesToZip(fileList, getLocation().toFile(), DSA_FILE_EXTENSIONS);
} catch (IOException e) {
e.printStackTrace();
} catch (TransformerException e) {
e.printStackTrace();
}
}
/* (non-Javadoc)
* @see org.eclipse.cdt.debug.edc.internal.snapshot.IAlbum#deleteSnapshot(org.eclipse.cdt.debug.edc.internal.snapshot.Snapshot)
*/
public void deleteSnapshot(Snapshot snap) {
NodeList snapMetaDataNode = document.getElementsByTagName(METADATA);
NodeList elementSnapshotList = ((Element) snapMetaDataNode.item(0)).getElementsByTagName(SNAPSHOT_LIST);
NodeList elementSnapshots = ((Element) snapMetaDataNode.item(0)).getElementsByTagName(SNAPSHOT);
int numSnapshots = elementSnapshots.getLength();
for (int i = 0; i < numSnapshots; i++) {
Element currentSnapshotNode = (Element) elementSnapshots.item(i);
String fileName = currentSnapshotNode.getAttribute("fileName");
if (fileName.equals(snap.getSnapshotFileName())) {
elementSnapshotList.item(0).removeChild(currentSnapshotNode);
break;
}
}
snapshotList.remove(snap);
saveAlbumData();
// refresh all data
try {
loadAlbum(true);
loadAlbumMetada(true);
} catch (Exception e) {
}
ZipFileUtils.deleteFileFromZip(snap.getSnapshotFileName(), getLocation().toFile(), DSA_FILE_EXTENSIONS);
}
@Override
public String toString() {
return "Album [name=" + name + ", launchName=" + launchName + ", sessionID=" + sessionID + "]";
}
public IPath createEmptyAlbum() {
IPath zipPath = null;
try {
zipPath = SnapshotUtils.getSnapshotsProject().getLocation();
zipPath = zipPath.append(getDefaultAlbumName());
zipPath = zipPath.addFileExtension("dsa");
boolean created = ZipFileUtils.createNewZip(zipPath.toFile());
if (created && zipPath.toFile().exists()){
setLocation(zipPath);
} else {
return null;
}
SnapshotUtils.getSnapshotsProject().refreshLocal(IResource.DEPTH_INFINITE, new NullProgressMonitor());
} catch (CoreException e) {
EDCDebugger.getMessageLogger().logError(e.getLocalizedMessage(), e);
}
return zipPath;
}
/* (non-Javadoc)
* @see org.eclipse.cdt.debug.edc.internal.snapshot.IAlbum#isRecording()
*/
public boolean isRecording() {
return recordingSessionID.length() > 0;
}
/**
* @noreference This method is not intended to be referenced by clients.
*/
public static void addSnapshotAlbumEventListener(ISnapshotAlbumEventListener listener) {
listeners.add(listener);
}
/**
* @noreference This method is not intended to be referenced by clients.
*/
public static void removeSnapshotAlbumEventListener(ISnapshotAlbumEventListener listener) {
listeners.remove(listener);
}
public static void captureSnapshotForSession(final DsfSession session) {
Job createSnapshotJob = new Job("Creating Debug Snapshot") {
@Override
protected IStatus run(final IProgressMonitor monitor) {
Query<IFrameDMContext> frameQuery = new Query<IFrameDMContext>() {
@Override
protected void execute(
DataRequestMonitor<IFrameDMContext> rm) {
DsfServicesTracker servicesTracker = new DsfServicesTracker(
EDCDebugger.getBundleContext(),
session.getId());
try {
RunControl runControl = servicesTracker.getService(RunControl.class);
IThreadDMContext[] suspendedThreads = runControl.getSuspendedThreads();
if (suspendedThreads.length == 0)
{
rm.setData(null);
rm.done();
}
else
{
Stack stackService = servicesTracker.getService(Stack.class);
if (stackService != null) {
stackService.getTopFrame(suspendedThreads[0], rm);
}
}
} finally {
servicesTracker.dispose();
}
}
};
session.getExecutor().execute(frameQuery);
IStatus status = Status.OK_STATUS;
try {
final StackFrameDMC stackFrame = (StackFrameDMC) frameQuery.get();
String sessionId = session.getId();
Album album = Album.getRecordingForSession(sessionId);
if (album == null) {
album = new Album();
album.setRecordingSessionID(sessionId);
}
final Album finalAlbum = album;
playSnapshotSound();
Query<IStatus> query = new Query<IStatus>() {
@Override
protected void execute(final DataRequestMonitor<IStatus> drm) {
try {
Snapshot newSnapshot = finalAlbum.createSnapshot(session, stackFrame, monitor);
// Fire the event to anyone listening
for (ISnapshotAlbumEventListener l : new ArrayList<ISnapshotAlbumEventListener>(listeners)) {
l.snapshotCreated(finalAlbum, newSnapshot, session, stackFrame);
}
drm.setData(Status.OK_STATUS);
} catch (Exception e) {
Status s = new Status(IStatus.ERROR, EDCDebugger.getUniqueIdentifier(), "Error creating snapshot.", e);
EDCDebugger.getMessageLogger().log(s);
drm.setStatus(s);
}
drm.done();
}
};
session.getExecutor().execute(query);
status = query.get();
} catch (Exception e) {
status = new Status(Status.ERROR, EDCDebugger.PLUGIN_ID, "Error creating snapshot", e);
}
return status;
}
};
createSnapshotJob.schedule();
}
public boolean isPlayingSnapshots() {
return playingSnapshots;
}
public void setPlayingSnapshots(boolean playingSnapshots) {
this.playingSnapshots = playingSnapshots;
}
public void stopPlayingSnapshots() {
setPlayingSnapshots(false);
openSnapshot(getCurrentSnapshotIndex()); // Reloading the current snapshot will resync the UI.
}
}