Bug 447554 - gcov not handling linked files

- Fix number of runs counter to come from program summary
  instead of object summary as that has been obsoleted
- Fix logic to handle case where we end up with an IFileEditorInput
  and we want an IFileStore (don't return null and end up
  using a source-not-found editor)
- Add a method to get all projects we are tracking for gcov
- Add a visitor class to search a project for a linked-in
  resource we are trying to open from the gcov view

Change-Id: Ieeb4c8a047a95da239e7b625f717a1d44fad8f31
Reviewed-on: https://git.eclipse.org/r/35155
Reviewed-by: Roland Grunberg <rgrunber@redhat.com>
Tested-by: Roland Grunberg <rgrunber@redhat.com>
(cherry picked from commit 75704836bd061a8d66588792170d9528a0946b3c)
Reviewed-on: https://git.eclipse.org/r/35156
Tested-by: Hudson CI
Reviewed-by: Jeff Johnston <jjohnstn@redhat.com>
Tested-by: Jeff Johnston <jjohnstn@redhat.com>
diff --git a/gcov/org.eclipse.linuxtools.gcov.core/src/org/eclipse/linuxtools/internal/gcov/parser/CovManager.java b/gcov/org.eclipse.linuxtools.gcov.core/src/org/eclipse/linuxtools/internal/gcov/parser/CovManager.java
index 42894f6..3f5d45d 100644
--- a/gcov/org.eclipse.linuxtools.gcov.core/src/org/eclipse/linuxtools/internal/gcov/parser/CovManager.java
+++ b/gcov/org.eclipse.linuxtools.gcov.core/src/org/eclipse/linuxtools/internal/gcov/parser/CovManager.java
@@ -157,7 +157,8 @@
 
         // to fill the view title
         if (daRcrd != null)
-            nbrPgmRuns = daRcrd.getObjSmryNbrPgmRuns();
+        	nbrPgmRuns = daRcrd.getPgmSmryNbrPgmRuns();
+
         /* process counts from data parsed */
 
         // solve graph for each function
diff --git a/gcov/org.eclipse.linuxtools.gcov.core/src/org/eclipse/linuxtools/internal/gcov/parser/GcdaRecordsParser.java b/gcov/org.eclipse.linuxtools.gcov.core/src/org/eclipse/linuxtools/internal/gcov/parser/GcdaRecordsParser.java
index a4c901a..107d13a 100644
--- a/gcov/org.eclipse.linuxtools.gcov.core/src/org/eclipse/linuxtools/internal/gcov/parser/GcdaRecordsParser.java
+++ b/gcov/org.eclipse.linuxtools.gcov.core/src/org/eclipse/linuxtools/internal/gcov/parser/GcdaRecordsParser.java
@@ -36,6 +36,7 @@
     private final ArrayList<GcnoFunction> fnctns;
     private long objSmryNbrPgmRuns = 0;
     private long pgmSmryChksm = 0;
+    private long pgmSmryNbrPgmRuns = 0;
     private long objSmryChksm = 0;
     private long objSmryArcCnts = 0;
     private long objSmrytotalCnts = 0;
@@ -212,7 +213,9 @@
                 case GCOV_TAG_PROGRAM_SUMMARY: {
                     // long[] pgmSmryskips = new long[(int) length];
                     pgmSmryChksm = (stream.readInt() & MasksGenerator.UNSIGNED_INT_MASK);
-                    for (int i = 0; i < length - 1; i++) {
+                    stream.readInt();
+                    pgmSmryNbrPgmRuns = (stream.readInt() & MasksGenerator.UNSIGNED_INT_MASK);
+                    for (int i = 0; i < length - 3; i++) {
                         // pgmSmryskips[i] = (stream.readInt() & MasksGenerator.UNSIGNED_INT_MASK);
                         stream.readInt();
                     }
@@ -269,14 +272,22 @@
      * @return the objSmrySumMax
      */
     public long getObjSmrySumMax() {
-        return objSmrySumMax;
+    	return objSmrySumMax;
     }
 
     /**
      * @return the pgmSmryChksm
      */
     public long getPgmSmryChksm() {
-        return pgmSmryChksm;
+    	return pgmSmryChksm;
     }
 
+    /**
+     * @return the prgSmryNbrPgmRuns
+     */
+    public long getPgmSmryNbrPgmRuns() {
+    	return pgmSmryNbrPgmRuns;
+    }
+
+
 }
diff --git a/gcov/org.eclipse.linuxtools.gcov.core/src/org/eclipse/linuxtools/internal/gcov/view/annotatedsource/GcovAnnotationModel.java b/gcov/org.eclipse.linuxtools.gcov.core/src/org/eclipse/linuxtools/internal/gcov/view/annotatedsource/GcovAnnotationModel.java
index ceb6892..50f7567 100644
--- a/gcov/org.eclipse.linuxtools.gcov.core/src/org/eclipse/linuxtools/internal/gcov/view/annotatedsource/GcovAnnotationModel.java
+++ b/gcov/org.eclipse.linuxtools.gcov.core/src/org/eclipse/linuxtools/internal/gcov/view/annotatedsource/GcovAnnotationModel.java
@@ -17,11 +17,16 @@
 import java.util.Iterator;
 import java.util.List;
 
+import org.eclipse.cdt.core.model.CoreModel;
 import org.eclipse.cdt.core.model.IBinary;
 import org.eclipse.cdt.core.model.ICElement;
 import org.eclipse.cdt.core.model.ICProject;
 import org.eclipse.cdt.ui.CDTUITools;
 import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IResourceProxy;
+import org.eclipse.core.resources.IResourceProxyVisitor;
 import org.eclipse.core.runtime.CoreException;
 import org.eclipse.core.runtime.IPath;
 import org.eclipse.core.runtime.Path;
@@ -127,22 +132,20 @@
     }
 
     private void updateAnnotations(boolean force) {
-        // We do not annotate any editor displaying content of a project not tracked.
-        ICElement element = CDTUITools.getEditorInputCElement(editor.getEditorInput());
-        if (!GcovAnnotationModelTracker.getInstance().containsProject(element.getCProject().getProject())) {
-            return;
-        }
+    	// We used to not annotate any editor displaying content of an element whose project was not tracked.
+    	// This logic fails when we have a linked-in file which won't point back to a project that has
+    	// been registered so it has been removed.
 
-        SourceFile coverage = findSourceCoverageForEditor();
-        if (coverage != null) {
-            if (!annotated || force) {
-                createAnnotations(coverage);
-            }
-        } else {
-            if (annotated) {
-                clear();
-            }
-        }
+    	SourceFile coverage = findSourceCoverageForEditor();
+    	if (coverage != null) {
+    		if (!annotated || force) {
+    			createAnnotations(coverage);
+    		}
+    	} else {
+    		if (annotated) {
+    			clear();
+    		}
+    	}
     }
 
     private SourceFile findSourceCoverageForEditor() {
@@ -160,39 +163,94 @@
         return findSourceCoverageForElement(element);
     }
 
+    // Private resource proxy visitor to run through a project's resources to see if
+    // it contains a link to a C element's resource.  This allows us to locate the
+    // project (and it's binary) that has gcov data for a particular resource that has been linked into
+    // the project.  We can't just query the resource for it's project in such a case.  This
+    // is part of the fix for bug: 447554
+    private class FindLinkedResourceVisitor implements IResourceProxyVisitor {
+
+    	final private ICElement element;
+    	private boolean keepSearching = true;
+    	private boolean found;
+
+    	public FindLinkedResourceVisitor(ICElement element) {
+    		this.element = element;
+    	}
+
+    	public boolean foundElement() {
+    		return found;
+    	}
+
+    	@Override
+    	public boolean visit(IResourceProxy proxy) {
+    		if (proxy.isLinked() && proxy.requestResource().getLocationURI().equals(element.getLocationURI())) {
+    			found = true;
+    			keepSearching = false;
+    		}
+    		return keepSearching;
+    	}
+
+    }
+
     private SourceFile findSourceCoverageForElement(ICElement element) {
-        List<SourceFile> sources = new ArrayList<> ();
-        ICProject cProject = element.getCProject();
-        IPath target = GcovAnnotationModelTracker.getInstance().getBinaryPath(cProject.getProject());
-        try {
-            IBinary[] binaries = cProject.getBinaryContainer().getBinaries();
-            for (IBinary b : binaries) {
-                if (b.getResource().getLocation().equals(target)) {
-                    CovManager covManager = new CovManager(b.getResource().getLocation().toOSString());
-                    covManager.processCovFiles(covManager.getGCDALocations(), null);
-                    sources.addAll(covManager.getAllSrcs());
-                }
-            }
-        } catch (IOException|CoreException|InterruptedException e) {
-        }
+    	List<SourceFile> sources = new ArrayList<> ();
+    	ICProject cProject = element.getCProject();
+    	IPath target = GcovAnnotationModelTracker.getInstance().getBinaryPath(cProject.getProject());
+    	if (target == null) {
+    		// We cannot find a target for this element, using it's project.
+    		// This can be caused by linking in a file to the project which may
+    		// not have a project or may point to another unseen project if the file originated
+    		// there.
+    		IProject[] trackedProjects = GcovAnnotationModelTracker.getInstance().getTrackedProjects();
+    		for (IProject proj : trackedProjects) {
+    			// Look at all projects that are registered for gcov viewing and see if the
+    			// element is linked in.
+    			try {
+    				FindLinkedResourceVisitor visitor = new FindLinkedResourceVisitor(element);
+    				proj.accept(visitor, IResource.DEPTH_INFINITE);
+    				// If we find a match, make note of the target and the real C project.
+    				if (visitor.foundElement()) {
+    					target = GcovAnnotationModelTracker.getInstance().getBinaryPath(proj);
+    					cProject = CoreModel.getDefault().getCModel().getCProject(proj.getName());
+    					break;
+    				}
+    			} catch (CoreException e) {
+    			}
+    		}
+    		if (target == null)
+    			return null;
+    	}
 
-        for (SourceFile sf : sources) {
-            IPath sfPath = new Path(sf.getName());
-            IFile file = STLink2SourceSupport.getFileForPath(sfPath, cProject.getProject());
-            if (file != null && element.getResource().getLocation().equals(file.getLocation())) {
-                return sf;
-            }
-        }
+    	try {
+    		IBinary[] binaries = cProject.getBinaryContainer().getBinaries();
+    		for (IBinary b : binaries) {
+    			if (b.getResource().getLocation().equals(target)) {
+    				CovManager covManager = new CovManager(b.getResource().getLocation().toOSString());
+    				covManager.processCovFiles(covManager.getGCDALocations(), null);
+    				sources.addAll(covManager.getAllSrcs());
+    			}
+    		}
+    	} catch (IOException|CoreException|InterruptedException e) {
+    	}
 
-        IPath binFolder = target.removeLastSegments(1);
-        for (SourceFile sf : sources) {
-            String sfPath = Paths.get(binFolder.toOSString()).resolve(sf.getName()).normalize().toString();
-            if (sfPath.equals(element.getLocationURI().getPath())) {
-                return sf;
-            }
-        }
+    	for (SourceFile sf : sources) {
+    		IPath sfPath = new Path(sf.getName());
+    		IFile file = STLink2SourceSupport.getFileForPath(sfPath, cProject.getProject());
+    		if (file != null && element.getResource().getLocation().equals(file.getLocation())) {
+    			return sf;
+    		}
+    	}
 
-        return null;
+    	IPath binFolder = target.removeLastSegments(1);
+    	for (SourceFile sf : sources) {
+    		String sfPath = Paths.get(binFolder.toOSString()).resolve(sf.getName()).normalize().toString();
+    		if (sfPath.equals(element.getLocationURI().getPath())) {
+    			return sf;
+    		}
+    	}
+
+    	return null;
     }
 
     private void createAnnotations(SourceFile sourceFile) {
diff --git a/gcov/org.eclipse.linuxtools.gcov.core/src/org/eclipse/linuxtools/internal/gcov/view/annotatedsource/GcovAnnotationModelTracker.java b/gcov/org.eclipse.linuxtools.gcov.core/src/org/eclipse/linuxtools/internal/gcov/view/annotatedsource/GcovAnnotationModelTracker.java
index 06d3208..04ee132 100644
--- a/gcov/org.eclipse.linuxtools.gcov.core/src/org/eclipse/linuxtools/internal/gcov/view/annotatedsource/GcovAnnotationModelTracker.java
+++ b/gcov/org.eclipse.linuxtools.gcov.core/src/org/eclipse/linuxtools/internal/gcov/view/annotatedsource/GcovAnnotationModelTracker.java
@@ -126,7 +126,11 @@
     }
 
     public void addProject (IProject project, IPath binary) {
-        trackedProjects.put(project, binary);
+    	trackedProjects.put(project, binary);
+    }
+
+    public IProject[] getTrackedProjects() {
+    	return trackedProjects.keySet().toArray(new IProject[0]);
     }
 
     public void dispose() {
diff --git a/gcov/org.eclipse.linuxtools.gcov.core/src/org/eclipse/linuxtools/internal/gcov/view/annotatedsource/OpenSourceFileAction.java b/gcov/org.eclipse.linuxtools.gcov.core/src/org/eclipse/linuxtools/internal/gcov/view/annotatedsource/OpenSourceFileAction.java
index aa421fb..ca7656d 100644
--- a/gcov/org.eclipse.linuxtools.gcov.core/src/org/eclipse/linuxtools/internal/gcov/view/annotatedsource/OpenSourceFileAction.java
+++ b/gcov/org.eclipse.linuxtools.gcov.core/src/org/eclipse/linuxtools/internal/gcov/view/annotatedsource/OpenSourceFileAction.java
@@ -29,6 +29,7 @@
 import org.eclipse.linuxtools.internal.gcov.parser.SourceFile;
 import org.eclipse.ui.IEditorInput;
 import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.IFileEditorInput;
 import org.eclipse.ui.IURIEditorInput;
 import org.eclipse.ui.IWorkbenchPage;
 import org.eclipse.ui.PartInitException;
@@ -46,17 +47,24 @@
 
     // FIXME: move this method in binutils plugin.
     private static IFileStore getFileStore(IProject project, IPath path) {
-        IEditorInput input = STLink2SourceSupport.getEditorInput(path, project);
-        if (input instanceof IURIEditorInput) {
-            IURIEditorInput editorInput = (IURIEditorInput) input;
-            URI uri = editorInput.getURI();
-            try {
-                return EFS.getStore(uri);
-            } catch (CoreException e) {
-                return null;
-            }
-        }
-        return null;
+    	IEditorInput input = STLink2SourceSupport.getEditorInput(path, project);
+    	if (input instanceof IURIEditorInput) {
+    		IURIEditorInput editorInput = (IURIEditorInput) input;
+    		URI uri = editorInput.getURI();
+    		try {
+    			return EFS.getStore(uri);
+    		} catch (CoreException e) {
+    			return null;
+    		}
+    	} else if (input instanceof IFileEditorInput) {
+    		IFile f = ((IFileEditorInput) input).getFile();
+    		try {
+    			return EFS.getStore(f.getLocationURI());
+    		} catch (CoreException e) {
+    			return null;
+    		}
+    	}
+    	return null;
     }
 
     public static void openAnnotatedSourceFile(IProject project, IFile binary, SourceFile sourceFile, int lineNumber) {