Skip to content

Commit 1335053

Browse files
Therzoknulltoken
authored andcommitted
Improve tracking toolset of improperly released unmanaged handles
1 parent bf464cf commit 1335053

File tree

10 files changed

+157
-40
lines changed

10 files changed

+157
-40
lines changed

.travis.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ install: ./CI/travis.${TRAVIS_OS_NAME}.install.deps.sh
1818

1919
# Build libgit2, LibGit2Sharp and run the tests
2020
script:
21-
- ./build.libgit2sharp.sh
21+
- ./build.libgit2sharp.sh 'LEAKS_IDENTIFYING'
2222

2323
# Only watch the development branch
2424
branches:

CI/build.msbuild

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,14 @@
99
<UsingTask AssemblyFile="$(RootDir)\Lib\xUnit\xunit.runner.msbuild.dll"
1010
TaskName="Xunit.Runner.MSBuild.xunit" />
1111
<Target Name="Clean">
12-
<Message Text="Commit SHA = $(CommitSha)" />
12+
<Message Text="Commit SHA = $(CommitSha)" />
1313

14-
<WriteLinesToFile Condition="'$(CommitSha)' != ''"
15-
File="$(RootDir)\LibGit2Sharp\libgit2sharp_hash.txt"
16-
Lines="$(CommitSha)"
17-
Overwrite="true" />
14+
<WriteLinesToFile Condition="'$(CommitSha)' != ''"
15+
File="$(RootDir)\LibGit2Sharp\libgit2sharp_hash.txt"
16+
Lines="$(CommitSha)"
17+
Overwrite="true" />
1818

19-
<!-- Workaround for xbuild -->
19+
<!-- Workaround for xbuild -->
2020
<Exec Condition=" ('$(OS)' != 'Windows_NT') " Command=" rm -r -f $(DeployFolder) " />
2121
<Exec Condition=" ('$(OS)' != 'Windows_NT') " Command=" rm -r -f $(TestBuildDir) " />
2222

@@ -29,10 +29,11 @@
2929
</Target>
3030

3131
<Target Name="Build" DependsOnTargets="Init">
32+
<Message Text="ExtraDefine = $(ExtraDefine)" />
3233
<MSBuild
3334
Projects="$(RootDir)\LibGit2Sharp.sln"
3435
Targets="Build"
35-
Properties="Configuration=$(Configuration);TrackFileAccess=false" />
36+
Properties="Configuration=$(Configuration);TrackFileAccess=false;ExtraDefine=$(ExtraDefine)" />
3637
</Target>
3738

3839
<Target Name="Test" DependsOnTargets="Build">

LibGit2Sharp.Tests/LibGit2Sharp.Tests.csproj

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,8 @@
127127
<PropertyGroup>
128128
<NativeBinariesDirectory>$(MSBuildProjectDirectory)\..\Lib\NativeBinaries</NativeBinariesDirectory>
129129
</PropertyGroup>
130-
<Import Project="$(MSBuildProjectDirectory)\..\LibGit2Sharp\CopyWindowsNativeDependencies.targets" />
130+
<Import Project="..\LibGit2Sharp\CopyWindowsNativeDependencies.targets" />
131+
<Import Project="..\LibGit2Sharp\ExtraDefine.targets" />
131132
<PropertyGroup>
132133
<PreBuildEvent>
133134
</PreBuildEvent>

LibGit2Sharp.Tests/TestHelpers/BaseFixture.cs

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,13 @@ public class BaseFixture : IPostTestDirectoryRemover, IDisposable
1616
{
1717
private readonly List<string> directories = new List<string>();
1818

19+
#if LEAKS_IDENTIFYING
20+
public BaseFixture()
21+
{
22+
LeaksContainer.Clear();
23+
}
24+
#endif
25+
1926
static BaseFixture()
2027
{
2128
// Do the set up in the static ctor so it only happens once
@@ -190,14 +197,22 @@ public void Register(string directoryPath)
190197

191198
public virtual void Dispose()
192199
{
193-
#if LEAKS
194-
GC.Collect();
195-
#endif
196-
197200
foreach (string directory in directories)
198201
{
199202
DirectoryHelper.DeleteDirectory(directory);
200203
}
204+
205+
#if LEAKS_IDENTIFYING
206+
GC.Collect();
207+
GC.WaitForPendingFinalizers();
208+
209+
if (LeaksContainer.TypeNames.Any())
210+
{
211+
Assert.False(true, string.Format("Some handles of the following types haven't been properly released: {0}.{1}"
212+
+ "In order to get some help fixing those leaks, uncomment the define LEAKS_TRACKING in SafeHandleBase.cs{1}"
213+
+ "and run the tests locally.", string.Join(", ", LeaksContainer.TypeNames), Environment.NewLine));
214+
}
215+
#endif
201216
}
202217

203218
protected static void InconclusiveIf(Func<bool> predicate, string message)

LibGit2Sharp/Core/Handles/SafeHandleBase.cs

Lines changed: 92 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,85 @@
1-
//#define LEAKS //Uncomment this or add a conditional symbol to activate this mode
1+

2+
// This activates a lightweight mode which will help put under the light
3+
// incorrectly released handles by outputing a warning message in the console.
4+
//
5+
// This should be activated when tests are being run of the CI server.
6+
//
7+
// Uncomment the line below or add a conditional symbol to activate this mode
8+
9+
//#define LEAKS_IDENTIFYING
10+
11+
// This activates a more throrough mode which will show the stack trace of the
12+
// allocation code path for each handle that has been improperly released.
13+
//
14+
// This should be manually activated when some warnings have been raised as
15+
// a result of LEAKS_IDENTIFYING mode activation.
16+
//
17+
// Uncomment the line below or add a conditional symbol to activate this mode
18+
19+
//#define LEAKS_TRACKING
220

321
using System;
22+
using System.Collections.Generic;
423
using System.Diagnostics;
524
using System.Globalization;
25+
using System.Linq;
626
using System.Runtime.ConstrainedExecution;
727
using System.Runtime.InteropServices;
828
using System.Threading;
929

30+
#if LEAKS_IDENTIFYING
31+
namespace LibGit2Sharp.Core
32+
{
33+
/// <summary>
34+
/// Holds leaked handle type names reported by <see cref="Core.Handles.SafeHandleBase"/>
35+
/// </summary>
36+
public static class LeaksContainer
37+
{
38+
private static readonly HashSet<string> _typeNames = new HashSet<string>();
39+
private static readonly object _lockpad = new object();
40+
41+
/// <summary>
42+
/// Report a new leaked handle type name
43+
/// </summary>
44+
/// <param name="typeName">Short name of the leaked handle type.</param>
45+
public static void Add(string typeName)
46+
{
47+
lock (_lockpad)
48+
{
49+
_typeNames.Add(typeName);
50+
}
51+
}
52+
53+
/// <summary>
54+
/// Removes all previously reported leaks.
55+
/// </summary>
56+
public static void Clear()
57+
{
58+
lock (_lockpad)
59+
{
60+
_typeNames.Clear();
61+
}
62+
}
63+
64+
/// <summary>
65+
/// Returns all reported leaked handle type names.
66+
/// </summary>
67+
public static IEnumerable<string> TypeNames
68+
{
69+
get { return _typeNames.ToArray(); }
70+
}
71+
}
72+
}
73+
#endif
74+
1075
namespace LibGit2Sharp.Core.Handles
1176
{
1277
internal abstract class SafeHandleBase : SafeHandle
1378
{
14-
#if LEAKS
79+
80+
#if LEAKS_TRACKING
1581
private readonly string trace;
82+
private readonly Guid id;
1683
#endif
1784

1885
/// <summary>
@@ -27,26 +94,40 @@ protected SafeHandleBase()
2794
{
2895
NativeMethods.AddHandle();
2996
registered = 1;
30-
#if LEAKS
97+
98+
#if LEAKS_TRACKING
99+
id = Guid.NewGuid();
100+
Trace.WriteLine(string.Format(CultureInfo.InvariantCulture, "Allocating {0} handle ({1})", GetType().Name, id));
31101
trace = new StackTrace(2, true).ToString();
32102
#endif
33103
}
34104

35-
#if DEBUG
36105
protected override void Dispose(bool disposing)
37106
{
38-
if (!disposing && !IsInvalid)
107+
bool leaked = !disposing && !IsInvalid;
108+
109+
#if LEAKS_IDENTIFYING
110+
if (leaked)
39111
{
40-
Trace.WriteLine(string.Format(CultureInfo.InvariantCulture, "A {0} handle wrapper has not been properly disposed.", GetType().Name));
41-
#if LEAKS
42-
Trace.WriteLine(trace);
43-
#endif
44-
Trace.WriteLine("");
112+
LeaksContainer.Add(GetType().Name);
45113
}
114+
#endif
46115

47116
base.Dispose(disposing);
48-
}
117+
118+
#if LEAKS_TRACKING
119+
if (!leaked)
120+
{
121+
Trace.WriteLine(string.Format(CultureInfo.InvariantCulture, "Disposing {0} handle ({1})", GetType().Name, id));
122+
}
123+
else
124+
{
125+
Trace.WriteLine(string.Format(CultureInfo.InvariantCulture, "Unexpected finalization of {0} handle ({1})", GetType().Name, id));
126+
Trace.WriteLine(trace);
127+
Trace.WriteLine("");
128+
}
49129
#endif
130+
}
50131

51132
// Prevent the debugger from evaluating this property because it has side effects
52133
[DebuggerBrowsable(DebuggerBrowsableState.Never)]

LibGit2Sharp/ExtraDefine.targets

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
3+
<!-- Add extra defines to DefineConstants. -->
4+
<PropertyGroup>
5+
<DefineConstants Condition=" '$(ExtraDefine)' != '' ">$(DefineConstants);$(ExtraDefine)</DefineConstants>
6+
</PropertyGroup>
7+
</Project>

LibGit2Sharp/LibGit2Sharp.csproj

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,7 @@
348348
<NativeBinariesDirectory>$(MSBuildProjectDirectory)\..\Lib\NativeBinaries</NativeBinariesDirectory>
349349
</PropertyGroup>
350350
<Import Project="CopyWindowsNativeDependencies.targets" />
351+
<Import Project="ExtraDefine.targets" />
351352
<PropertyGroup>
352353
<PreBuildEvent>
353354
</PreBuildEvent>
@@ -359,4 +360,4 @@
359360
<Target Name="AfterBuild">
360361
</Target>
361362
-->
362-
</Project>
363+
</Project>

build.libgit2sharp.cmd

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,34 @@
1+
@ECHO OFF
2+
3+
REM Sample usages:
4+
REM
5+
REM Building and running tests
6+
REM - build.libgit2sharp.cmd
7+
REM
8+
REM Building, running tests and embedding the libgit2sharp commit sha
9+
REM - build.libgit2sharp.cmd "6a6eb81272876fd63555165beef44de2aaa78a14"
10+
REM
11+
REM Building and identifying potential leaks while running tests
12+
REM - build.libgit2sharp.cmd "unknown" "LEAKS_IDENTIFYING"
13+
14+
115
SETLOCAL
216

317
SET BASEDIR=%~dp0
418
SET FrameworkVersion=v4.0.30319
519
SET FrameworkDir=%SystemRoot%\Microsoft.NET\Framework
20+
21+
if exist "%SystemRoot%\Microsoft.NET\Framework64" (
22+
SET FrameworkDir=%SystemRoot%\Microsoft.NET\Framework64
23+
)
24+
25+
ECHO ON
26+
627
SET CommitSha=%~1
28+
SET ExtraDefine=%~2
729

8-
"%FrameworkDir%\%FrameworkVersion%\msbuild.exe" "%BASEDIR%CI\build.msbuild" /property:CommitSha=%CommitSha%
30+
"%FrameworkDir%\%FrameworkVersion%\msbuild.exe" "%BASEDIR%CI\build.msbuild" /property:CommitSha=%CommitSha% /property:ExtraDefine="%ExtraDefine%"
931

1032
ENDLOCAL
1133

12-
EXIT /B %ERRORLEVEL%
34+
EXIT /B %ERRORLEVEL%

build.libgit2sharp.sh

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
LIBGIT2SHA=`cat ./LibGit2Sharp/libgit2_hash.txt`
44
SHORTSHA=${LIBGIT2SHA:0:7}
5+
EXTRADEFINE="$1"
56

67
rm -rf libgit2/build
78
mkdir libgit2/build
@@ -26,6 +27,6 @@ export MONO_OPTIONS=--debug
2627

2728
echo $DYLD_LIBRARY_PATH
2829
echo $LD_LIBRARY_PATH
29-
xbuild CI/build.msbuild /t:Deploy
30+
xbuild CI/build.msbuild /target:Deploy /property:ExtraDefine="$EXTRADEFINE"
3031

3132
exit $?

build.libgit2sharp.x64.cmd

Lines changed: 0 additions & 12 deletions
This file was deleted.

0 commit comments

Comments
 (0)