Skip to content

Commit fd06651

Browse files
committed
Native backtrace implementation
Better string handling in LLVM IR generator
1 parent a89f45b commit fd06651

File tree

9 files changed

+312
-143
lines changed

9 files changed

+312
-143
lines changed

src/Xamarin.Android.Build.Tasks/Tasks/LinkApplicationSharedLibraries.cs

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ namespace Xamarin.Android.Tasks
1616
{
1717
public class LinkApplicationSharedLibraries : AndroidAsyncTask
1818
{
19+
const int NDK_API_LEVEL = 21; // TODO: don't hardcode the API level
20+
1921
public override string TaskPrefix => "LAS";
2022

2123
sealed class Config
@@ -118,16 +120,38 @@ void RunLinker (Config config)
118120
IEnumerable<Config> GetLinkerConfigs ()
119121
{
120122
NdkTools ndk = null;
123+
string clangRuntimeDirTop = null;
124+
121125
if (EnableMarshalMethodTracing) {
122126
ndk = NdkTools.Create (AndroidNdkDirectory, logErrors: false, log: Log);
127+
128+
// Doesn't matter for which arch we run the compiler, they all share the same topmost runtime dir
129+
string clangPath = ndk.GetToolPath (NdkToolKind.CompilerCPlusPlus, AndroidTargetArch.Arm64, NDK_API_LEVEL);
130+
int ret = MonoAndroidHelper.RunProcess (
131+
clangPath, "-print-runtime-dir",
132+
(object sender, DataReceivedEventArgs e) => { // stdout
133+
if (clangRuntimeDirTop == null && !String.IsNullOrEmpty (e.Data)) {
134+
clangRuntimeDirTop = e.Data;
135+
}
136+
},
137+
(object sender, DataReceivedEventArgs e) => { // stderr
138+
if (!String.IsNullOrEmpty (e.Data)) {
139+
Log.LogError (e.Data);
140+
}
141+
}
142+
);
143+
144+
if (ret != 0) {
145+
Log.LogError ($"Failed to obtain clang runtime path from {clangPath}");
146+
}
123147
}
124148

125149
string runtimeNativeLibsDir = Path.GetFullPath (Path.Combine (AndroidBinUtilsDirectory, "..", "..", "..", "lib"));
126150
var abis = new Dictionary <string, InputFiles> (StringComparer.Ordinal);
127151
ITaskItem[] dsos = ApplicationSharedLibraries;
128152
foreach (ITaskItem item in dsos) {
129153
string abi = item.GetMetadata ("abi");
130-
abis [abi] = GatherFilesForABI (item.ItemSpec, abi, ObjectFiles, runtimeNativeLibsDir, ndk);
154+
abis [abi] = GatherFilesForABI (item.ItemSpec, abi, ObjectFiles, runtimeNativeLibsDir, ndk, clangRuntimeDirTop);
131155
}
132156

133157
const string commonLinkerArgs =
@@ -205,7 +229,7 @@ IEnumerable<Config> GetLinkerConfigs ()
205229
}
206230
}
207231

208-
InputFiles GatherFilesForABI (string runtimeSharedLibrary, string abi, ITaskItem[] objectFiles, string runtimeNativeLibsDir, NdkTools ndk)
232+
InputFiles GatherFilesForABI (string runtimeSharedLibrary, string abi, ITaskItem[] objectFiles, string runtimeNativeLibsDir, NdkTools ndk, string clangRuntimeDirTop)
209233
{
210234
List<string> extraLibraries = null;
211235

@@ -214,11 +238,22 @@ InputFiles GatherFilesForABI (string runtimeSharedLibrary, string abi, ITaskItem
214238
throw new ArgumentNullException (nameof (ndk));
215239
}
216240

217-
string libPath = ndk.GetDirectoryPath (NdkToolchainDir.PlatformLib, MonoAndroidHelper.AbiToTargetArch (abi), 21); // TODO: don't hardcode the API level
241+
AndroidTargetArch targetArch = MonoAndroidHelper.AbiToTargetArch (abi);
242+
string clangRuntimeAbi = MonoAndroidHelper.ArchToClangRuntimeAbi (targetArch);
243+
string clangLibraryAbi = MonoAndroidHelper.ArchToClangLibraryAbi (targetArch);
244+
string unwindLibPath = Path.GetFullPath (Path.Combine (clangRuntimeDirTop, clangRuntimeAbi, "libunwind.a"));
245+
string builtinsLibPath = Path.GetFullPath (Path.Combine (clangRuntimeDirTop, $"libclang_rt.builtins-{clangLibraryAbi}-android.a"));
246+
string libPath = ndk.GetDirectoryPath (NdkToolchainDir.PlatformLib, targetArch, NDK_API_LEVEL);
247+
string cxxAbiLibPath = Path.GetFullPath (Path.Combine (libPath, "..", "libc++abi.a"));
248+
218249
extraLibraries = new List<string> {
219250
Path.Combine (runtimeNativeLibsDir, MonoAndroidHelper.AbiToRid (abi), "libmarshal-methods-tracing.a"),
220251
$"-L \"{libPath}\"",
252+
$"\"{builtinsLibPath}\"",
253+
$"\"{cxxAbiLibPath}\"",
254+
$"\"{unwindLibPath}\"",
221255
"-lc",
256+
"-ldl",
222257
"-llog", // tracing uses android logger
223258
};
224259
}

src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/LlvmIrGenerator.Code.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -418,6 +418,9 @@ public void EmitLabel (LlvmIrFunction function, string labelName)
418418
} else {
419419
throw new InvalidOperationException ($"Unexpected pointer type in argument {i}, '{argument.Type}'");
420420
}
421+
} else if (argument.Value is string str) {
422+
StringSymbolInfo info = StringManager.Add (str);
423+
WriteGetStringPointer (info.SymbolName, info.Size, indent: false, detectBitness: true, skipPointerType: true);
421424
} else {
422425
Output.Write (argument.Value.ToString ());
423426
}

src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/LlvmIrGenerator.cs

Lines changed: 28 additions & 126 deletions
Original file line numberDiff line numberDiff line change
@@ -52,18 +52,6 @@ public PackedStructureMember (StructureMemberInfo<T> memberInfo, object? value,
5252
}
5353
}
5454

55-
public sealed class StringSymbolInfo
56-
{
57-
public readonly string SymbolName;
58-
public readonly ulong Size;
59-
60-
public StringSymbolInfo (string symbolName, ulong size)
61-
{
62-
SymbolName = symbolName;
63-
Size = size;
64-
}
65-
}
66-
6755
static readonly Dictionary<Type, string> typeMap = new Dictionary<Type, string> {
6856
{ typeof (bool), "i8" },
6957
{ typeof (byte), "i8" },
@@ -165,11 +153,13 @@ public StringSymbolInfo (string symbolName, ulong size)
165153
public AndroidTargetArch TargetArch { get; }
166154

167155
protected LlvmIrMetadataManager MetadataManager { get; }
156+
protected LlvmIrStringManager StringManager { get; }
168157

169158
protected LlvmIrGenerator (AndroidTargetArch arch, TextWriter output, string fileName)
170159
{
171160
Output = output;
172161
MetadataManager = new LlvmIrMetadataManager ();
162+
StringManager = new LlvmIrStringManager ();
173163
TargetArch = arch;
174164
Is64Bit = arch == AndroidTargetArch.X86_64 || arch == AndroidTargetArch.Arm64;
175165
this.fileName = fileName;
@@ -455,7 +445,7 @@ bool MaybeWriteStructureString<T> (StructureInfo<T> info, StructureMemberInfo<T>
455445
return false;
456446
}
457447

458-
StringSymbolInfo stringSymbol = WriteUniqueString ($"__{info.Name}_{smi.Info.Name}", str, ref structStringCounter);
448+
StringSymbolInfo stringSymbol = StringManager.Add (str, groupName: info.Name, symbolSuffix: smi.Info.Name);//WriteUniqueString ($"__{info.Name}_{smi.Info.Name}", str, ref structStringCounter);
459449
instance.AddPointerData (smi, stringSymbol.SymbolName, stringSymbol.Size);
460450

461451
return true;
@@ -622,12 +612,11 @@ public void WriteArray (IList<string> values, string symbolName, string? initial
622612
WriteEOL ();
623613
WriteEOL (initialComment ?? symbolName);
624614

625-
ulong arrayStringCounter = 0;
626-
var strings = new List<StringSymbolInfo> ();
615+
var strings = new List<StringSymbolInfo> ();
627616

628617
foreach (string s in values) {
629-
StringSymbolInfo symbol = WriteUniqueString ($"__{symbolName}", s, ref arrayStringCounter, LlvmIrVariableOptions.LocalConstexprString);
630-
strings.Add (new StringSymbolInfo (symbol.SymbolName, symbol.Size));
618+
StringSymbolInfo symbol = StringManager.Add (s, groupName: symbolName);
619+
strings.Add (symbol);
631620
}
632621

633622
if (strings.Count > 0) {
@@ -1137,12 +1126,12 @@ void WriteBufferToOutput (TextWriter? writer)
11371126
}
11381127
}
11391128

1140-
void WriteGetStringPointer (string? variableName, ulong size, bool indent = true, TextWriter? output = null)
1129+
void WriteGetStringPointer (string? variableName, ulong size, bool indent = true, TextWriter? output = null, bool detectBitness = false, bool skipPointerType = false)
11411130
{
1142-
WriteGetBufferPointer (variableName, "i8*", size, indent, output);
1131+
WriteGetBufferPointer (variableName, "i8*", size, indent, output, detectBitness, skipPointerType);
11431132
}
11441133

1145-
void WriteGetBufferPointer (string? variableName, string irType, ulong size, bool indent = true, TextWriter? output = null)
1134+
void WriteGetBufferPointer (string? variableName, string irType, ulong size, bool indent = true, TextWriter? output = null, bool detectBitness = false, bool skipPointerType = false)
11461135
{
11471136
output = EnsureOutput (output);
11481137
if (indent) {
@@ -1160,8 +1149,12 @@ void WriteGetBufferPointer (string? variableName, string irType, ulong size, boo
11601149
irBaseType = irType;
11611150
}
11621151

1152+
string indexType = detectBitness && Is64Bit ? "i64" : "i32";
11631153
// $"{irType} getelementptr inbounds ([{size} x {irBaseType}], [{size} x {irBaseType}]* @{variableName}, i32 0, i32 0)"
1164-
output.Write (irType);
1154+
if (!skipPointerType) {
1155+
output.Write (irType);
1156+
}
1157+
11651158
output.Write (" getelementptr inbounds ([");
11661159
output.Write (size);
11671160
output.Write (" x ");
@@ -1172,7 +1165,11 @@ void WriteGetBufferPointer (string? variableName, string irType, ulong size, boo
11721165
output.Write (irBaseType);
11731166
output.Write ("]* @");
11741167
output.Write (variableName);
1175-
output.Write (", i32 0, i32 0)");
1168+
output.Write (", ");
1169+
output.Write (indexType);
1170+
output.Write (" 0, ");
1171+
output.Write (indexType);
1172+
output.Write (" 0)");
11761173
}
11771174
}
11781175

@@ -1186,7 +1183,6 @@ public void WriteNameValueArray (string symbolName, IDictionary<string, string>
11861183

11871184
var strings = new List<StringSymbolInfo> ();
11881185
long i = 0;
1189-
ulong arrayStringCounter = 0;
11901186

11911187
foreach (var kvp in arrayContents) {
11921188
string name = kvp.Key;
@@ -1205,8 +1201,8 @@ public void WriteNameValueArray (string symbolName, IDictionary<string, string>
12051201

12061202
void WriteArrayString (string str, string symbolSuffix)
12071203
{
1208-
StringSymbolInfo symbol = WriteUniqueString ($"__{symbolName}_{symbolSuffix}", str, ref arrayStringCounter, LlvmIrVariableOptions.LocalConstexprString);
1209-
strings.Add (new StringSymbolInfo (symbol.SymbolName, symbol.Size));
1204+
StringSymbolInfo symbol = StringManager.Add (str, groupName: symbolName, symbolSuffix: symbolSuffix);
1205+
strings.Add (symbol);
12101206
}
12111207
}
12121208

@@ -1279,28 +1275,6 @@ public void WriteVariable<T> (string symbolName, T value, LlvmIrVariableOptions
12791275
Output.WriteLine (size);
12801276
}
12811277

1282-
/// <summary>
1283-
/// Writes a private string. Strings without symbol names aren't exported, but they may be referenced by other
1284-
/// symbols
1285-
/// </summary>
1286-
public string WriteString (string value)
1287-
{
1288-
return WriteString (value, LlvmIrVariableOptions.LocalString);
1289-
}
1290-
1291-
/// <summary>
1292-
/// Writes a string with automatically generated symbol name and symbol options (writeability, visibility etc) specified in the <paramref name="options"/> parameter.
1293-
/// </summary>
1294-
public string WriteString (string value, LlvmIrVariableOptions options)
1295-
{
1296-
string name = $"@.str";
1297-
if (stringCounter > 0) {
1298-
name += $".{stringCounter}";
1299-
}
1300-
stringCounter++;
1301-
return WriteString (name, value, options);
1302-
}
1303-
13041278
/// <summary>
13051279
/// Writes a global, C++ constexpr style string
13061280
/// </summary>
@@ -1317,101 +1291,27 @@ public string WriteString (string symbolName, string value, LlvmIrVariableOption
13171291
return WriteString (symbolName, value, options, out _);
13181292
}
13191293

1320-
/// <summary>
1321-
/// Writes a local, constexpr style string and returns its size in <paramref name="stringSize"/>
1322-
/// </summary>
1323-
public string WriteString (string symbolName, string value, out ulong stringSize)
1324-
{
1325-
return WriteString (symbolName, value, LlvmIrVariableOptions.LocalConstexprString, out stringSize);
1326-
}
1327-
13281294
/// <summary>
13291295
/// Writes a string with specified <paramref name="symbolName"/>, and symbol options (writeability, visibility etc) specified in the <paramref name="options"/>
13301296
/// parameter. Returns string size (in bytes) in <paramref name="stringSize"/>
13311297
/// </summary>
13321298
public string WriteString (string symbolName, string value, LlvmIrVariableOptions options, out ulong stringSize)
13331299
{
1334-
string strSymbolName;
1335-
bool global = options.IsGlobal;
1336-
if (global) {
1337-
strSymbolName = $"__{symbolName}";
1338-
} else {
1339-
strSymbolName = symbolName;
1340-
}
1341-
1342-
string quotedString = QuoteString (value, out stringSize);
1343-
1344-
// It might seem counter-intuitive that when we're requested to write a global string, here we generate a **local** one,
1345-
// but global strings are actually pointers to local storage.
1346-
WriteGlobalSymbolStart (strSymbolName, global ? LlvmIrVariableOptions.LocalConstexprString : options);
1347-
1348-
// WriteLine $"[{stringSize} x i8] c{quotedString}, align {GetAggregateAlignment (1, stringSize)}"
1349-
Output.Write ('[');
1350-
Output.Write (stringSize);
1351-
Output.Write (" x i8] c");
1352-
Output.Write (quotedString);
1353-
Output.Write (", align ");
1354-
Output.WriteLine (GetAggregateAlignment (1, stringSize));
1355-
1356-
if (!global) {
1300+
StringSymbolInfo info = StringManager.Add (value, groupName: symbolName);
1301+
stringSize = info.Size;
1302+
if (!options.IsGlobal) {
13571303
return symbolName;
13581304
}
13591305

13601306
string indexType = Is64Bit ? "i64" : "i32";
13611307
WriteGlobalSymbolStart (symbolName, LlvmIrVariableOptions.GlobalConstantStringPointer);
1362-
1363-
// WriteLine $"i8* getelementptr inbounds ([{stringSize} x i8], [{stringSize} x i8]* @{strSymbolName}, {indexType} 0, {indexType} 0), align {GetAggregateAlignment (PointerSize, stringSize)}"
1364-
Output.Write ("i8* getelementptr inbounds ([");
1365-
Output.Write (stringSize);
1366-
Output.Write (" x i8], [");
1367-
Output.Write (stringSize);
1368-
Output.Write (" x i8]* @");
1369-
Output.Write (strSymbolName);
1370-
Output.Write (", ");
1371-
Output.Write (indexType);
1372-
Output.Write (" 0, ");
1373-
Output.Write (indexType);
1374-
Output.Write (" 0), align ");
1308+
WriteGetStringPointer (info.SymbolName, info.Size, indent: false, detectBitness: true);
1309+
Output.Write (", align ");
13751310
Output.WriteLine (GetAggregateAlignment (PointerSize, stringSize));
13761311

13771312
return symbolName;
13781313
}
13791314

1380-
/// <summary>
1381-
/// Writes a string, creating a new symbol if the <paramref name="value"/> is unique or returns name of a previously created symbol with the same
1382-
/// string value. If a new symbol is written, its name is constructed by combining prefix (<paramref name="potentialSymbolNamePrefix"/>) with value
1383-
/// of a string counter referenced by the <paramref name="counter"/> parameter. Symbol is created as a local, C++ constexpr style string.
1384-
/// </summary>
1385-
public StringSymbolInfo WriteUniqueString (string potentialSymbolName, string value, ref ulong counter)
1386-
{
1387-
return WriteUniqueString (potentialSymbolName, value, ref counter, LlvmIrVariableOptions.LocalConstexprString);
1388-
}
1389-
1390-
/// <summary>
1391-
/// Writes a string, creating a new symbol if the <paramref name="value"/> is unique or returns name of a previously created symbol with the same
1392-
/// string value. If a new symbol is written, its name is constructed by combining prefix (<paramref name="potentialSymbolNamePrefix"/>) with value
1393-
/// of a string counter referenced by the <paramref name="counter"/> parameter. Symbol options (writeability, visibility etc) are specified in the <paramref
1394-
/// name="options"/> parameter. String size (in bytes) is returned in <paramref name="stringSize"/>.
1395-
/// </summary>
1396-
public StringSymbolInfo WriteUniqueString (string potentialSymbolNamePrefix, string value, ref ulong counter, LlvmIrVariableOptions options)
1397-
{
1398-
if (value == null) {
1399-
return null;
1400-
}
1401-
1402-
StringSymbolInfo info;
1403-
if (stringSymbolCache.TryGetValue (value, out info)) {
1404-
return info;
1405-
}
1406-
1407-
string newSymbolName = $"{potentialSymbolNamePrefix}.{counter++}";
1408-
WriteString (newSymbolName, value, options, out ulong stringSize);
1409-
info = new StringSymbolInfo (newSymbolName, stringSize);
1410-
stringSymbolCache.Add (value, info);
1411-
1412-
return info;
1413-
}
1414-
14151315
public virtual void WriteFileTop ()
14161316
{
14171317
WriteCommentLine ($"ModuleID = '{fileName}'");
@@ -1423,7 +1323,9 @@ public virtual void WriteFileTop ()
14231323
public virtual void WriteFileEnd ()
14241324
{
14251325
Output.WriteLine ();
1326+
StringManager.Flush (this);
14261327

1328+
Output.WriteLine ();
14271329
WriteAttributeSets ();
14281330

14291331
foreach (LlvmIrMetadataItem metadata in MetadataManager.Items) {

0 commit comments

Comments
 (0)