Skip to content

Commit

Permalink
Reduce number of reg invocations on Unity (#3365)
Browse files Browse the repository at this point in the history
  • Loading branch information
nirinchev authored Jul 7, 2023
1 parent 387c019 commit c1186dd
Show file tree
Hide file tree
Showing 4 changed files with 95 additions and 84 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
* Added validation checks to the geospatial type constructors. This means that an exception will now be thrown when constructing an invalid geospatial shape rather than when using it in a query. (PR [#3362](https://github.com/realm/realm-dotnet/pull/3362))

### Fixed
* None
* Fixed an issue on Unity on Windows when the weaver would trigger excessive terminal windows to open. (Issue [3364]https://github.com/realm/realm-dotnet/issues/3364)
* Fixed an issue on Unity on CI where weaving would fail with the following error: `Could not analyze the user's assembly. Cannot access a closed Stream.`. (Issue [3364]https://github.com/realm/realm-dotnet/issues/3364)

### Compatibility
* Realm Studio: 13.0.0 or later.
Expand Down
12 changes: 6 additions & 6 deletions Realm/Realm.Weaver/Analytics/Analytics.cs
Original file line number Diff line number Diff line change
Expand Up @@ -249,8 +249,8 @@ private void AnalyzeUserAssembly(ModuleDefinition module)
try
{
// collect environment details
_realmEnvMetrics[UserEnvironment.UserId] = GetAnonymizedUserId();
_realmEnvMetrics[UserEnvironment.LegacyUserId] = GetLegacyAnonymizedUserId();
_realmEnvMetrics[UserEnvironment.UserId] = AnonymizedUserId;
_realmEnvMetrics[UserEnvironment.LegacyUserId] = LegacyAnonymizedUserId;
_realmEnvMetrics[UserEnvironment.ProjectId] = SHA256Hash(Encoding.UTF8.GetBytes(_config.ProjectId ?? module.Assembly.Name.Name));
_realmEnvMetrics[UserEnvironment.RealmSdk] = "dotnet";
_realmEnvMetrics[UserEnvironment.RealmSdkVersion] = Assembly.GetExecutingAssembly().GetName().Version.ToString();
Expand Down Expand Up @@ -435,12 +435,12 @@ public async Task SubmitAnalytics()
{
var payload = "Analytics disabled";

// this is necessary since when not in the assembly that has the models
// AnalyzeRealmClassProperties won't be called
_analyzeUserAssemblyTask.Wait();

if (_config.AnalyticsCollection != AnalyticsCollection.Disabled)
{
// this is necessary since when not in the assembly that has the models
// AnalyzeRealmClassProperties won't be called
_analyzeUserAssemblyTask.Wait();

try
{
const string sendAddr = "https://data.mongodb-api.com/app/realmsdkmetrics-zmhtm/endpoint/v2/metric?data=";
Expand Down
137 changes: 71 additions & 66 deletions Realm/Realm.Weaver/Analytics/AnalyticsUtils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,75 @@ namespace RealmWeaver
{
internal static class AnalyticsUtils
{
private static readonly Lazy<string> _anonymizedUserId = new(() =>
{
var id = string.Empty;
try
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
var machineIdToParse = RunProcess("reg", "QUERY HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Cryptography -v MachineGuid");
var regex = new Regex("\\s+MachineGuid\\s+\\w+\\s+((\\w+-?)+)", RegexOptions.Multiline);
var match = regex.Match(machineIdToParse);

if (match.Groups.Count > 1)
{
id = match.Groups[1].Value;
}
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
var machineIdToParse = RunProcess("ioreg", "-rd1 -c IOPlatformExpertDevice");
var regex = new Regex(".*\\\"IOPlatformUUID\\\"\\s=\\s\\\"(.+)\\\"", RegexOptions.Multiline);
var match = regex.Match(machineIdToParse);

if (match.Groups.Count > 1)
{
id = match.Groups[1].Value;
}
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
id = File.ReadAllText("/etc/machine-id");
}

if (id.Length == 0)
{
return Unknown();
}

// We're salting the id with an hardcoded byte array just to avoid that a machine is recognizable across
// unrelated projects that use the same mechanics to obtain a machine's ID
const string salt = "Realm is great";
var saltedId = Encoding.UTF8.GetBytes(id + salt);
return SHA256Hash(saltedId);
}
catch
{
return Unknown();
}
});

private static readonly Lazy<string> _legacyAnonymizedUserId = new(() =>
{
try
{
var id = NetworkInterface.GetAllNetworkInterfaces()
.Where(n => n.Name == "en0" || (n.OperationalStatus == OperationalStatus.Up && n.NetworkInterfaceType != NetworkInterfaceType.Loopback))
.Select(n => n.GetPhysicalAddress().GetAddressBytes())
.First();
return SHA256Hash(id, useLegacyEncoding: true);
}
catch
{
return Unknown();
}
});

public static string AnonymizedUserId => _anonymizedUserId.Value;

public static string LegacyAnonymizedUserId => _legacyAnonymizedUserId.Value;

public static string GetTargetOsName(FrameworkName frameworkName)
{
var targetOs = frameworkName.Identifier;
Expand Down Expand Up @@ -184,72 +253,6 @@ public static string InferLanguageVersion(string netFramework, string netFramewo
return Unknown();
}

// Knowledge on unique machine Ids for different OSes obtained from https://github.com/denisbrodbeck/machineid
public static string GetAnonymizedUserId()
{
var id = string.Empty;
try
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
var machineIdToParse = RunProcess("reg", "QUERY HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Cryptography -v MachineGuid");
var regex = new Regex("\\s+MachineGuid\\s+\\w+\\s+((\\w+-?)+)", RegexOptions.Multiline);
var match = regex.Match(machineIdToParse);

if (match.Groups.Count > 1)
{
id = match.Groups[1].Value;
}
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
var machineIdToParse = RunProcess("ioreg", "-rd1 -c IOPlatformExpertDevice");
var regex = new Regex(".*\\\"IOPlatformUUID\\\"\\s=\\s\\\"(.+)\\\"", RegexOptions.Multiline);
var match = regex.Match(machineIdToParse);

if (match.Groups.Count > 1)
{
id = match.Groups[1].Value;
}
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
id = File.ReadAllText("/etc/machine-id");
}

if (id.Length == 0)
{
return Unknown();
}

// We're salting the id with an hardcoded byte array just to avoid that a machine is recognizable across
// unrelated projects that use the same mechanics to obtain a machine's ID
const string salt = "Realm is great";
var saltedId = Encoding.UTF8.GetBytes(id + salt);
return SHA256Hash(saltedId);
}
catch
{
return Unknown();
}
}

public static string GetLegacyAnonymizedUserId()
{
try
{
var id = NetworkInterface.GetAllNetworkInterfaces()
.Where(n => n.Name == "en0" || (n.OperationalStatus == OperationalStatus.Up && n.NetworkInterfaceType != NetworkInterfaceType.Loopback))
.Select(n => n.GetPhysicalAddress().GetAddressBytes())
.First();
return SHA256Hash(id, useLegacyEncoding: true);
}
catch
{
return Unknown();
}
}

private static bool ContainsIgnoreCase(this string @this, string strCompare) =>
@this.IndexOf(strCompare, StringComparison.OrdinalIgnoreCase) > -1;

Expand All @@ -263,6 +266,8 @@ private static string RunProcess(string filename, string arguments)
Arguments = arguments,
UseShellExecute = false,
RedirectStandardOutput = true,
CreateNoWindow = true,
WindowStyle = ProcessWindowStyle.Hidden,
#if DEBUG
RedirectStandardError = true,
#endif
Expand Down
27 changes: 16 additions & 11 deletions Realm/Realm.Weaver/RealmWeaver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -180,29 +180,39 @@ public Weaver(ModuleDefinition module, ILogger logger, string framework)

public WeaveModuleResult Execute(Analytics.Config analyticsConfig)
{
_logger.Debug("Weaving file: " + _moduleDefinition.FileName);

var analytics = new Analytics(analyticsConfig, _references, _logger, _moduleDefinition);

var result = ExecuteInternal(analyticsConfig, out var weaveResults);
analytics.AnalyzeRealmClassProperties(weaveResults);

// Don't wait for submission
_ = analytics.SubmitAnalytics();

return result;
}

private WeaveModuleResult ExecuteInternal(Analytics.Config analyticsConfig, out WeaveTypeResult[] weaveResults)
{
_logger.Debug("Weaving file: " + _moduleDefinition.FileName);

// This is necessary because some frameworks, e.g. xamarin, have the models in one assembly and the platform
// specific code in another assembly, but we still want to report what target the user is building for
if (_references.Realm == null)
{
// Don't wait for submission
_ = analytics.SubmitAnalytics();

weaveResults = Array.Empty<WeaveTypeResult>();
return WeaveModuleResult.Skipped($"Not weaving assembly '{_moduleDefinition.Assembly.Name}' because it doesn't reference Realm.");
}

var isWoven = _moduleDefinition.Assembly.CustomAttributes.Any(a => a.AttributeType.IsSameAs(_references.WovenAssemblyAttribute));
if (isWoven)
{
weaveResults = Array.Empty<WeaveTypeResult>();
return WeaveModuleResult.Skipped($"Not weaving assembly '{_moduleDefinition.Assembly.Name}' because it has already been processed.");
}

var matchingTypes = GetMatchingTypes().ToArray();

var weaveResults = matchingTypes.Select(matchingType =>
weaveResults = matchingTypes.Select(matchingType =>
{
var type = matchingType.Type;
var isGenerated = matchingType.IsGenerated;
Expand All @@ -223,11 +233,6 @@ public WeaveModuleResult Execute(Analytics.Config analyticsConfig)
var wovenAssemblyAttribute = new CustomAttribute(_references.WovenAssemblyAttribute_Constructor);
_moduleDefinition.Assembly.CustomAttributes.Add(wovenAssemblyAttribute);

analytics.AnalyzeRealmClassProperties(weaveResults);

// Don't wait for submission
_ = analytics.SubmitAnalytics();

var failedResults = weaveResults.Where(r => !r.IsSuccessful).ToArray();
if (failedResults.Any())
{
Expand Down

0 comments on commit c1186dd

Please sign in to comment.