changeset 101:52fc27842bc1

fixing TW-45209, TW-45095; report resulting artifact path for each pdb file instead of file name from build agent - will fix performance when locating pdb file on server - will fix file name collisions when publishing several pdb's with the same name in scope of single build
author Evgeniy.Koshkin@unit-519.Labs.IntelliJ.Net
date Mon, 16 May 2016 19:22:22 +0300
parents d9cb6eec6f74
children 0e927b14ccf1
files .idea/libraries/jdom.xml .idea/misc.xml agent/src/jetbrains/buildServer/symbols/SymbolsIndexer.java common/common.iml common/src/jetbrains/buildServer/symbols/PdbSignatureIndexEntry.java common/src/jetbrains/buildServer/symbols/PdbSignatureIndexUtil.java common/src/jetbrains/buildServer/symbols/SymbolsConstants.java server/src/jetbrains/buildServer/symbols/BuildSymbolsIndexProvider.java
diffstat 8 files changed, 149 insertions(+), 58 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/.idea/libraries/jdom.xml	Mon May 16 19:22:22 2016 +0300
@@ -0,0 +1,9 @@
+<component name="libraryTable">
+  <library name="jdom">
+    <CLASSES>
+      <root url="jar://$teamcitydistribution$/webapps/ROOT/WEB-INF/lib/jdom.jar!/" />
+    </CLASSES>
+    <JAVADOC />
+    <SOURCES />
+  </library>
+</component>
\ No newline at end of file
--- a/.idea/misc.xml	Mon May 16 15:03:56 2016 +0300
+++ b/.idea/misc.xml	Mon May 16 19:22:22 2016 +0300
@@ -27,7 +27,7 @@
   <component name="ProjectResources">
     <default-html-doctype>http://www.w3.org/1999/xhtml</default-html-doctype>
   </component>
-  <component name="ProjectRootManager" version="2" languageLevel="JDK_1_5" default="false" assert-keyword="true" jdk-15="true" project-jdk-name="1.8" project-jdk-type="JavaSDK">
+  <component name="ProjectRootManager" version="2" languageLevel="JDK_1_6" default="false" assert-keyword="true" jdk-15="true" project-jdk-name="1.8" project-jdk-type="JavaSDK">
     <output url="file://$PROJECT_DIR$/out" />
   </component>
 </project>
\ No newline at end of file
--- a/agent/src/jetbrains/buildServer/symbols/SymbolsIndexer.java	Mon May 16 15:03:56 2016 +0300
+++ b/agent/src/jetbrains/buildServer/symbols/SymbolsIndexer.java	Mon May 16 19:22:22 2016 +0300
@@ -9,16 +9,16 @@
 import jetbrains.buildServer.util.EventDispatcher;
 import jetbrains.buildServer.util.FileUtil;
 import org.apache.log4j.Logger;
+import org.jdom.JDOMException;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
 import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
 import java.io.IOException;
-import java.util.Collection;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
 
 /**
  * @author Evgeniy.Koshkin
@@ -33,7 +33,7 @@
 
   @NotNull private final ArtifactsWatcher myArtifactsWatcher;
   @NotNull private final JetSymbolsExe myJetSymbolsExe;
-  @NotNull private final Collection<File> mySymbolsToProcess = new CopyOnWriteArrayList<File>();
+  @NotNull private final Map<File, String> myFileToArtifactMapToProcess = new ConcurrentHashMap<File, String>();
 
   @Nullable private BuildProgressLogger myProgressLogger;
   @Nullable private File myBuildTempDirectory;
@@ -73,26 +73,31 @@
       public void afterAtrifactsPublished(@NotNull AgentRunningBuild build, @NotNull BuildFinishedStatus buildStatus) {
         super.afterAtrifactsPublished(build, buildStatus);
         if(!isIndexingApplicable()) return;
-        if (mySymbolsToProcess.isEmpty()) {
+        if (myFileToArtifactMapToProcess.isEmpty()) {
           myProgressLogger.warning("Symbols weren't found in artifacts to be published.");
           LOG.debug("Symbols weren't found in artifacts to be published for build with id " + build.getBuildId());
         } else {
           myProgressLogger.message("Collecting symbol files signatures.");
           LOG.debug("Collecting symbol files signatures.");
           try {
-            final File symbolSignaturesFile = FileUtil.createTempFile(myBuildTempDirectory, "symbol-signatures-", ".xml", false);
-            myJetSymbolsExe.dumpGuidsToFile(mySymbolsToProcess, symbolSignaturesFile, myProgressLogger);
-            if(symbolSignaturesFile.exists()){
+            final Set<PdbSignatureIndexEntry> signatureLocalFilesData = getSignatures(myFileToArtifactMapToProcess.keySet());
+            if(!signatureLocalFilesData.isEmpty()){
+              final Set<PdbSignatureIndexEntry> indexData = new HashSet<PdbSignatureIndexEntry>();
+              for(PdbSignatureIndexEntry signatureIndexEntry : signatureLocalFilesData){
+                indexData.add(new PdbSignatureIndexEntry(signatureIndexEntry.getGuid(), myFileToArtifactMapToProcess.get(new File(signatureIndexEntry.getArtifactPath()))));
+              }
+              final File indexDataFile = FileUtil.createTempFile(myBuildTempDirectory, SymbolsConstants.SYMBOL_SIGNATURES_FILE_NAME_PREFIX, ".xml", false);
+              PdbSignatureIndexUtil.write(new FileOutputStream(indexDataFile), indexData);
               myProgressLogger.message("Publishing collected symbol files signatures.");
-              myArtifactsWatcher.addNewArtifactsPath(symbolSignaturesFile + "=>" + ".teamcity/symbols");
+              myArtifactsWatcher.addNewArtifactsPath(indexDataFile + "=>" + ".teamcity/symbols");
             }
-          } catch (IOException e) {
+          } catch (Exception e) {
             LOG.error("Error while dumping symbols/binaries signatures for build with id " + build.getBuildId(), e);
             myProgressLogger.error("Error while dumping symbols/binaries signatures.");
             myProgressLogger.exception(e);
           }
         }
-        mySymbolsToProcess.clear();
+        myFileToArtifactMapToProcess.clear();
       }
     });
   }
@@ -106,19 +111,19 @@
     }
 
     LOG.debug("Searching for symbol files in publishing artifacts.");
-    final Collection<File> pdbFiles = getArtifactPathsByFileExtension(artifacts, PDB_FILE_EXTENSION);
+    final Map<File, String> pdbFiles = getArtifactPathsByFileExtension(artifacts, PDB_FILE_EXTENSION);
     if(pdbFiles.isEmpty()) return;
 
     final PdbFilePatcher pdbFilePatcher = new PdbFilePatcher(myBuildTempDirectory, mySrcSrvHomeDir, new SrcSrvStreamBuilder(myFileUrlProvider, myProgressLogger));
-    for(File pdbFile : pdbFiles){
-      if(mySymbolsToProcess.contains(pdbFile)){
+    for(File pdbFile : pdbFiles.keySet()){
+      if(myFileToArtifactMapToProcess.containsKey(pdbFile)){
         LOG.debug(String.format("File %s already processed. Skipped.", pdbFile.getAbsolutePath()));
         continue;
       }
       try {
         myProgressLogger.message("Indexing sources appeared in file " + pdbFile.getAbsolutePath());
         pdbFilePatcher.patch(pdbFile, myProgressLogger);
-        mySymbolsToProcess.add(pdbFile);
+        myFileToArtifactMapToProcess.put(pdbFile, pdbFiles.get(pdbFile));
       } catch (Throwable e) {
         LOG.error("Error occurred while patching symbols file " + pdbFile, e);
         myProgressLogger.error("Error occurred while patching symbols file " + pdbFile);
@@ -127,6 +132,18 @@
     }
   }
 
+  private Set<PdbSignatureIndexEntry> getSignatures(Collection<File> files) throws IOException, JDOMException {
+    final File guidDumpFile = FileUtil.createTempFile(myBuildTempDirectory, "symbol-signatures-local-", ".xml", false);
+    myJetSymbolsExe.dumpGuidsToFile(files, guidDumpFile, myProgressLogger);
+    if(guidDumpFile.exists()){
+      myArtifactsWatcher.addNewArtifactsPath(guidDumpFile + "=>" + ".teamcity/symbols");
+    }
+    if(guidDumpFile.isFile())
+      return PdbSignatureIndexUtil.read(new FileInputStream(guidDumpFile));
+    else
+      return Collections.emptySet();
+  }
+
   @Nullable
   private File getSrcSrvHomeDir(@NotNull AgentRunningBuild build) {
     final Map<String,String> agentConfigParameters = build.getAgentConfiguration().getConfigurationParameters();
@@ -143,13 +160,14 @@
     return null;
   }
 
-  private Collection<File> getArtifactPathsByFileExtension(List<ArtifactsCollection> artifactsCollections, String fileExtension){
-    final Collection<File> result = new HashSet<File>();
+  private Map<File, String> getArtifactPathsByFileExtension(List<ArtifactsCollection> artifactsCollections, String fileExtension){
+    final Map<File, String> result = new HashMap<File, String>();
     for(ArtifactsCollection artifactsCollection : artifactsCollections){
       if(artifactsCollection.isEmpty()) continue;
-      for (File artifact : artifactsCollection.getFilePathMap().keySet()){
+      final Map<File, String> filePathMap = artifactsCollection.getFilePathMap();
+      for (final File artifact : filePathMap.keySet()){
         if(FileUtil.getExtension(artifact.getPath()).equalsIgnoreCase(fileExtension))
-          result.add(artifact);
+          result.put(artifact, filePathMap.get(artifact));
       }
     }
     return result;
--- a/common/common.iml	Mon May 16 15:03:56 2016 +0300
+++ b/common/common.iml	Mon May 16 19:22:22 2016 +0300
@@ -8,8 +8,7 @@
     <orderEntry type="inheritedJdk" />
     <orderEntry type="sourceFolder" forTests="false" />
     <orderEntry type="library" name="idea annotations.jar" level="project" />
-    <orderEntry type="library" name="Idea-OpenApi" level="project" />
+    <orderEntry type="library" name="jdom" level="project" />
     <orderEntry type="library" name="Common-Api" level="project" />
   </component>
-</module>
-
+</module>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common/src/jetbrains/buildServer/symbols/PdbSignatureIndexEntry.java	Mon May 16 19:22:22 2016 +0300
@@ -0,0 +1,26 @@
+package jetbrains.buildServer.symbols;
+
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * Created by Evgeniy.Koshkin.
+ */
+public class PdbSignatureIndexEntry {
+    private final String myGuid;
+    private final String myArtifactPath;
+
+    public PdbSignatureIndexEntry(@NotNull String guid, @NotNull String artifactPath) {
+        myGuid = guid;
+        myArtifactPath = artifactPath;
+    }
+
+    @NotNull
+    public String getGuid() {
+        return myGuid;
+    }
+
+    @NotNull
+    public String getArtifactPath() {
+        return myArtifactPath;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common/src/jetbrains/buildServer/symbols/PdbSignatureIndexUtil.java	Mon May 16 19:22:22 2016 +0300
@@ -0,0 +1,50 @@
+package jetbrains.buildServer.symbols;
+
+import jetbrains.buildServer.util.XmlUtil;
+import org.jdom.Document;
+import org.jdom.Element;
+import org.jdom.JDOMException;
+import org.jdom.input.SAXBuilder;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.*;
+
+/**
+ * Created by Evgeniy.Koshkin.
+ */
+class PdbSignatureIndexUtil {
+    private static final String SIGN = "sign";
+    private static final String FILE = "file";
+    private static final String FILE_SIGNS = "file-signs";
+    private static final String FILE_SIGN_ENTRY = "file-sign-entry";
+
+    @NotNull
+    static Set<PdbSignatureIndexEntry> read(@NotNull final InputStream inputStream) throws JDOMException, IOException {
+        final SAXBuilder builder = new SAXBuilder();
+        final Document document = builder.build(inputStream);
+        final Set<PdbSignatureIndexEntry> result = new HashSet<PdbSignatureIndexEntry>();
+        for (Object signElementObject : document.getRootElement().getChildren()){
+            final Element signElement = (Element) signElementObject;
+            result.add(new PdbSignatureIndexEntry(extractGuid(signElement.getAttributeValue(SIGN)), signElement.getAttributeValue(FILE)));
+        }
+        return result;
+    }
+
+    static void write(@NotNull final OutputStream outputStream, @NotNull final Set<PdbSignatureIndexEntry> indexData) throws IOException {
+        final Element root = new Element(FILE_SIGNS);
+        for (final PdbSignatureIndexEntry indexEntry : indexData){
+            final Element entry = new Element(FILE_SIGN_ENTRY);
+            entry.setAttribute(SIGN, indexEntry.getGuid());
+            entry.setAttribute(FILE, indexEntry.getArtifactPath());
+            root.addContent(root);
+        }
+        XmlUtil.saveDocument(new Document(root), outputStream);
+    }
+
+    private static String extractGuid(String sign) {
+        return sign.substring(0, sign.length() - 1).toLowerCase(); //last symbol is PEDebugType
+    }
+}
--- a/common/src/jetbrains/buildServer/symbols/SymbolsConstants.java	Mon May 16 15:03:56 2016 +0300
+++ b/common/src/jetbrains/buildServer/symbols/SymbolsConstants.java	Mon May 16 19:22:22 2016 +0300
@@ -11,4 +11,6 @@
 
   public static final String APP_SYMBOLS = "/app/symbols/";
   public static final String APP_SOURCES = "/app/sources/";
+
+  public static final String SYMBOL_SIGNATURES_FILE_NAME_PREFIX = "symbol-signatures-artifacts-";
 }
--- a/server/src/jetbrains/buildServer/symbols/BuildSymbolsIndexProvider.java	Mon May 16 15:03:56 2016 +0300
+++ b/server/src/jetbrains/buildServer/symbols/BuildSymbolsIndexProvider.java	Mon May 16 19:22:22 2016 +0300
@@ -9,17 +9,14 @@
 import jetbrains.buildServer.util.CollectionsUtil;
 import jetbrains.buildServer.util.filters.Filter;
 import org.apache.log4j.Logger;
-import org.jdom.Document;
-import org.jdom.Element;
 import org.jdom.JDOMException;
-import org.jdom.input.SAXBuilder;
 import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
 
 import java.io.IOException;
-import java.io.InputStream;
 import java.util.Collections;
 import java.util.HashMap;
-import java.util.Map;
+import java.util.Set;
 import java.util.concurrent.atomic.AtomicReference;
 
 /**
@@ -44,7 +41,7 @@
     final BuildArtifact symbols = sBuild.getArtifacts(BuildArtifactsViewMode.VIEW_HIDDEN_ONLY).getArtifact(".teamcity/symbols");
     final BuildArtifact symbolSignaturesSource = symbols == null ? null : CollectionsUtil.findFirst(symbols.getChildren(), new Filter<BuildArtifact>() {
       public boolean accept(@NotNull BuildArtifact data) {
-        return data.getName().startsWith("symbol-signatures");
+        return data.getName().startsWith(SymbolsConstants.SYMBOL_SIGNATURES_FILE_NAME_PREFIX);
       }
     });
     if(symbolSignaturesSource == null) {
@@ -52,54 +49,44 @@
       return;
     }
 
-    Map<String, String> indexData = Collections.emptyMap();
+    Set<PdbSignatureIndexEntry> indexEntries = Collections.emptySet();
     try {
-      indexData = readIndex(symbolSignaturesSource.getInputStream());
+      indexEntries = PdbSignatureIndexUtil.read(symbolSignaturesSource.getInputStream());
     } catch (IOException e) {
       LOG.debug("Failed to read symbols index data from artifact " + symbolSignaturesSource.getRelativePath(), e);
     } catch (JDOMException e) {
       LOG.debug("Failed to read symbols index data from artifact " + symbolSignaturesSource.getRelativePath(), e);
     }
 
-    LOG.debug(String.format("Build with id %d provides %d symbol file signatures.", buildId, indexData.size()));
+    LOG.debug(String.format("Build with id %d provides %d symbol file signatures.", buildId, indexEntries.size()));
 
-    for (String signature : indexData.keySet()) {
-      final String fileName = indexData.get(signature);
-      final String artifactPath = locateArtifact(sBuild, fileName);
+    for (final PdbSignatureIndexEntry indexEntry : indexEntries) {
+      final String signature = indexEntry.getGuid();
+      final String artifactPathOrName = indexEntry.getArtifactPath();
+      final String artifactPath = locateArtifact(sBuild, artifactPathOrName);
       if (artifactPath == null) {
-        LOG.debug(String.format("Failed to find artifact by name %s and build id %d.", fileName, buildId));
+        LOG.debug(String.format("Failed to find artifact by name %s and build id %d.", artifactPathOrName, buildId));
         continue;
       }
       final HashMap<String, String> data = new HashMap<String, String>();
       data.put(ARTIFACT_PATH_KEY, artifactPath);
-      data.put(FILE_NAME_KEY, fileName);
+      data.put(FILE_NAME_KEY, artifactPathOrName);
       metadataStorageWriter.addParameters(signature, data);
-      LOG.debug("Stored symbol file signature " + signature + " for file name " + fileName + " build id " + buildId);
+      LOG.debug("Stored symbol file signature " + signature + " for file name " + artifactPathOrName + " build id " + buildId);
     }
   }
 
-  @NotNull
-  private Map<String, String> readIndex(InputStream inputStream) throws JDOMException, IOException {
-    SAXBuilder builder = new SAXBuilder();
-    Document document = builder.build(inputStream);
-    Map<String, String> result = new HashMap<String, String>();
-    for (Object signElementObject : document.getRootElement().getChildren()){
-      final Element signElement = (Element) signElementObject;
-      result.put(extractGuid(signElement.getAttributeValue("sign")), signElement.getAttributeValue("file"));
-    }
-    return result;
-  }
+  @Nullable
+  private String locateArtifact(@NotNull SBuild build, final @NotNull String artifactPathOrName) {
+    final BuildArtifacts artifacts = build.getArtifacts(BuildArtifactsViewMode.VIEW_DEFAULT_WITH_ARCHIVES_CONTENT);
+    final BuildArtifact artifact = artifacts.getArtifact(artifactPathOrName);
+    if(artifact != null) return artifactPathOrName;
 
-  private String extractGuid(String sign) {
-    return sign.substring(0, sign.length() - 1).toLowerCase(); //last symbol is PEDebugType
-  }
-
-  private String locateArtifact(SBuild build, final String fileName) {
     final AtomicReference<String> locatedArtifactPath = new AtomicReference<String>(null);
-    build.getArtifacts(BuildArtifactsViewMode.VIEW_DEFAULT_WITH_ARCHIVES_CONTENT).iterateArtifacts(new BuildArtifacts.BuildArtifactsProcessor() {
+    artifacts.iterateArtifacts(new BuildArtifacts.BuildArtifactsProcessor() {
       @NotNull
       public Continuation processBuildArtifact(@NotNull BuildArtifact artifact) {
-        if(artifact.getName().equals(fileName)){
+        if(artifact.getName().equals(artifactPathOrName)){
           locatedArtifactPath.set(artifact.getRelativePath());
           return Continuation.BREAK;
         }