Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add devdeviceid telemetry #1480

Merged
merged 7 commits into from
Sep 10, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions include/vcpkg/base/parse.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ namespace vcpkg
return is_lower_alpha(ch) || is_ascii_digit(ch) || ch == '-';
}

static constexpr bool is_hex_digit_lower(char32_t ch) { return is_ascii_digit(ch) || (ch >= 'a' && ch <= 'f'); }

static constexpr bool is_hex_digit(char32_t ch)
{
return is_ascii_digit(ch) || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F');
vicroms marked this conversation as resolved.
Show resolved Hide resolved
Expand Down
17 changes: 17 additions & 0 deletions include/vcpkg/base/system.deviceid.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#pragma once

#include <vcpkg/base/fwd/files.h>
#include <vcpkg/base/fwd/stringview.h>

#include <string>

namespace vcpkg
{
bool validate_device_id(StringView uuid);

#if defined(_WIN32)
std::string get_device_id();
#else
std::string get_device_id(const vcpkg::Filesystem& fs);
#endif
vicroms marked this conversation as resolved.
Show resolved Hide resolved
}
2 changes: 2 additions & 0 deletions include/vcpkg/base/system.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ namespace vcpkg

const ExpectedL<Path>& get_home_dir() noexcept;

const ExpectedL<Path>& get_platform_cache_root() noexcept;

const ExpectedL<Path>& get_platform_cache_vcpkg() noexcept;

const ExpectedL<Path>& get_user_configuration_home() noexcept;
Expand Down
1 change: 1 addition & 0 deletions include/vcpkg/metrics.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ namespace vcpkg
CommandName,
DeploymentKind,
DetectedCiEnvironment,
DevDeviceId,
CiProjectId,
CiOwnerId,
InstallPlan_1,
Expand Down
18 changes: 18 additions & 0 deletions src/vcpkg-test/metrics.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#include <vcpkg-test/util.h>

#include <vcpkg/base/system.deviceid.h>

#include <vcpkg/metrics.h>

#include <set>
Expand Down Expand Up @@ -88,6 +90,22 @@ TEST_CASE ("user config parses multiple paragraphs ", "[metrics]")
CHECK(result.last_completed_survey == "survey");
}

TEST_CASE ("device id", "[metrics]")
{
CHECK(validate_device_id("c5337d65-1e69-46e1-af76-bffc7b9ff40a"));

CHECK_FALSE(validate_device_id(""));
CHECK_FALSE(validate_device_id("nope"));
CHECK_FALSE(validate_device_id("xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"));
CHECK_FALSE(validate_device_id("c5337d65-1e69-46e1-af76-bffc7b9ff40a "));
CHECK_FALSE(validate_device_id("c5337d6--1e6--46e--af76--ffc7b9ff40a"));
CHECK_FALSE(validate_device_id("c5337d65-1e69-46e1-af76-bffc7b9ff4\r\n"));
CHECK_FALSE(validate_device_id("c5337d65-1e69-46e1-af76-bffc7b9ff4\0"));
CHECK_FALSE(validate_device_id("C5337D65-1E69-46E1-AF76-BFFC7b9ff40A"));
CHECK_FALSE(validate_device_id("{c5337d65-1e69-46e1-af76-bffc7b9ff40a}"));
CHECK_FALSE(validate_device_id("c5337d65:1e69:46e1:af76:bffc7b9ff40a"));
}

TEST_CASE ("user config to string", "[metrics]")
{
MetricsUserConfig uut;
Expand Down
18 changes: 17 additions & 1 deletion src/vcpkg/base/system.cpp
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
#include <vcpkg/base/checks.h>
#include <vcpkg/base/contractual-constants.h>
#include <vcpkg/base/expected.h>
#include <vcpkg/base/files.h>
#include <vcpkg/base/messages.h>
#include <vcpkg/base/path.h>
#include <vcpkg/base/system.debug.h>
#include <vcpkg/base/system.h>
#include <vcpkg/base/uuid.h>

#if defined(__APPLE__)
#include <sys/sysctl.h>
Expand Down Expand Up @@ -504,6 +506,17 @@ namespace vcpkg
}
#endif

const ExpectedL<Path>& get_platform_cache_root() noexcept
vicroms marked this conversation as resolved.
Show resolved Hide resolved
{
return
#if defined(_WIN32)
get_appdata_local()
#else
get_xdg_cache_home()
#endif
;
}

const ExpectedL<Path>& get_platform_cache_vcpkg() noexcept
{
static ExpectedL<Path> s_vcpkg =
Expand Down Expand Up @@ -556,9 +569,12 @@ namespace vcpkg
case REG_SZ:
case REG_EXPAND_SZ:
// remove trailing nulls
while (!value->data.empty() && !value->data.back())
while (
vicroms marked this conversation as resolved.
Show resolved Hide resolved
!value->data.empty() && value->data.size() >= 2 &&
!(L'\0' != *reinterpret_cast<const wchar_t*>(value->data.data() + value->data.size() - 2)))
{
value->data.pop_back();
value->data.pop_back();
}

{
Expand Down
119 changes: 119 additions & 0 deletions src/vcpkg/base/system.deviceid.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
#include <vcpkg/base/expected.h>
#include <vcpkg/base/files.h>
#include <vcpkg/base/parse.h>
#include <vcpkg/base/strings.h>
#include <vcpkg/base/stringview.h>
#include <vcpkg/base/system.deviceid.h>
#include <vcpkg/base/system.h>
#include <vcpkg/base/uuid.h>

namespace vcpkg
{
// To ensure consistency, the uuid must follow the format specified below.
// - The value follows the 8-4-4-4-12 format(xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx)
// - The value shall be all lowercase and only contain hyphens. No braces or brackets.
bool validate_device_id(StringView uuid)
{
static constexpr size_t UUID_LENGTH = 36;
static constexpr char format[] = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx";
if (uuid.size() != UUID_LENGTH) return false;
for (size_t i = 0; i < UUID_LENGTH; ++i)
{
if (format[i] == '-' && uuid[i] != '-') return false;
if (format[i] == 'x' && !ParserBase::is_hex_digit_lower(uuid[i])) return false;
}
return true;
}

#if defined(_WIN32)
// Returns a shared DevDeviceID for telemetry.
std::string get_device_id()
{
// The value is cached in the 64-bit Windows Registry under HKeyCurrentUser\SOFTWARE\Microsoft\DeveloperTools.
// The key should be named 'deviceid' and should be of type REG_SZ(String value).
// The value should be stored in plain text.
auto maybe_registry_value =
get_registry_string(HKEY_CURRENT_USER, "SOFTWARE\\Microsoft\\DeveloperTools", "deviceid");
if (auto registry_value = maybe_registry_value.get())
{
auto device_id = *registry_value;
vicroms marked this conversation as resolved.
Show resolved Hide resolved
return validate_device_id(device_id) ? device_id : std::string{};
}

// vcpkg::generate_random_UUID() generates a compliant UUID
vicroms marked this conversation as resolved.
Show resolved Hide resolved
auto new_device_id = Strings::ascii_to_lowercase(vcpkg::generate_random_UUID());
const auto as_utf16 = Strings::to_utf16(new_device_id);

const auto status = RegSetKeyValueW(HKEY_CURRENT_USER,
L"SOFTWARE\\Microsoft\\DeveloperTools",
L"deviceid",
REG_SZ,
as_utf16.c_str(),
static_cast<DWORD>((1 + as_utf16.size()) * sizeof(wchar_t)));
return (status != ERROR_SUCCESS) ? std::string{} : new_device_id;
}
#else
std::string get_device_id(const vcpkg::Filesystem& fs)
{
/* On Linux:
* - The folder subpath will be /Microsoft/DeveloperTools
* - Use $XDG_CACHE_HOME if it is set and not empty, else use $HOME/.cache.
* - The file will be called 'deviceid'. The value should be stored in plain text, UTF-8.
*
* On MacOS:
* - The folder path will be $HOME\Library\Application Support\Microsoft\DeveloperTools where $HOME is the
* user's home directory.
* - The file will be called 'deviceid'.
* - The value should be stored in plain text, UTF-8.
*/
const auto maybe_home_path = vcpkg::get_platform_cache_root();
if (!maybe_home_path)
{
return {};
}

auto home_path = maybe_home_path.get();
const auto container_path =
#if defined(__APPLE__)
*home_path / "Library/Application Support/Microsoft/DeveloperTools"
#else
*home_path / "Microsoft/DeveloperTools"
#endif
;
const auto id_file_path = container_path / "deviceid";

std::error_code ec;
auto maybe_file = fs.exists(id_file_path, ec);
if (ec)
{
return {};
}

if (maybe_file)
{
auto contents = fs.read_contents(id_file_path, ec);
if (ec || !validate_device_id(contents))
{
return {};
}
return contents;
}

// vcpkg::generate_random_UUID() generates a compliant UUID
auto new_device_id = Strings::ascii_to_lowercase(vcpkg::generate_random_UUID());
fs.create_directories(container_path, ec);
if (ec)
{
return {};
}

fs.write_contents(id_file_path, new_device_id, ec);
if (ec)
{
return {};
}

return new_device_id;
}
#endif
}
12 changes: 12 additions & 0 deletions src/vcpkg/metrics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include <vcpkg/base/json.h>
#include <vcpkg/base/strings.h>
#include <vcpkg/base/system.debug.h>
#include <vcpkg/base/system.deviceid.h>
#include <vcpkg/base/system.h>
#include <vcpkg/base/system.mac.h>
#include <vcpkg/base/system.process.h>
Expand Down Expand Up @@ -131,6 +132,7 @@ namespace vcpkg
{StringMetric::CommandName, "command_name", "z-preregister-telemetry"},
{StringMetric::DeploymentKind, "deployment_kind", "Git"},
{StringMetric::DetectedCiEnvironment, "detected_ci_environment", "Generic"},
{StringMetric::DevDeviceId, "devdeviceid", "00000000-0000-0000-0000-000000000000"},
{StringMetric::CiProjectId, "ci_project_id", "0"},
{StringMetric::CiOwnerId, "ci_owner_id", "0"},
// spec:triplet:version,...
Expand Down Expand Up @@ -578,6 +580,16 @@ namespace vcpkg
auto session = MetricsSessionData::from_system();

auto submission = get_global_metrics_collector().get_submission();

auto deviceid =
#if defined(_WIN32)
get_device_id()
#else
get_device_id(fs)
#endif
;
submission.track_string(StringMetric::DevDeviceId, deviceid);

const std::string payload = format_metrics_payload(user, session, submission);
if (g_should_print_metrics.load())
{
Expand Down
Loading