/src/perfetto/src/base/uuid.cc
Line | Count | Source |
1 | | /* |
2 | | * Copyright (C) 2019 The Android Open Source Project |
3 | | * |
4 | | * Licensed under the Apache License, Version 2.0 (the "License"); |
5 | | * you may not use this file except in compliance with the License. |
6 | | * You may obtain a copy of the License at |
7 | | * |
8 | | * http://www.apache.org/licenses/LICENSE-2.0 |
9 | | * |
10 | | * Unless required by applicable law or agreed to in writing, software |
11 | | * distributed under the License is distributed on an "AS IS" BASIS, |
12 | | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
13 | | * See the License for the specific language governing permissions and |
14 | | * limitations under the License. |
15 | | */ |
16 | | |
17 | | #include "perfetto/ext/base/uuid.h" |
18 | | |
19 | | #include <random> |
20 | | |
21 | | #include "perfetto/base/time.h" |
22 | | #include "perfetto/ext/base/utils.h" |
23 | | |
24 | | namespace perfetto { |
25 | | namespace base { |
26 | | namespace { |
27 | | |
28 | | constexpr char kHexmap[] = {'0', '1', '2', '3', '4', '5', '6', '7', |
29 | | '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; |
30 | | |
31 | | } // namespace |
32 | | |
33 | | // A globally unique 128-bit number. |
34 | | // In the early days of perfetto we were (sorta) respecting rfc4122. Later we |
35 | | // started replacing the LSB of the UUID with the statsd subscription ID in |
36 | | // other parts of the codebase (see perfetto_cmd.cc) for the convenience of |
37 | | // trace lookups, so rfc4122 made no sense as it just reduced entropy. |
38 | 152 | Uuid Uuidv4() { |
39 | | // Mix different sources of entropy to reduce the chances of collisions. |
40 | | // Only using boot time is not enough. Under the assumption that most traces |
41 | | // are started around the same time at boot, within a 1s window, the birthday |
42 | | // paradox gives a chance of 90% collisions with 70k traces over a 1e9 space |
43 | | // (Number of ns in a 1s window). |
44 | | // We deliberately don't use /dev/urandom as that might block for |
45 | | // unpredictable time if the system is idle (and is not portable). |
46 | | // The UUID does NOT need to be cryptographically secure, but random enough |
47 | | // to avoid collisions across a large number of devices. |
48 | 152 | uint64_t boot_ns = static_cast<uint64_t>(GetBootTimeNs().count()); |
49 | 152 | uint64_t epoch_ns = static_cast<uint64_t>(GetWallTimeNs().count()); |
50 | | |
51 | | // Use code ASLR as entropy source. |
52 | 152 | uint32_t code_ptr = |
53 | 152 | static_cast<uint32_t>(reinterpret_cast<uint64_t>(&Uuidv4) >> 12); |
54 | | |
55 | | // Use stack ASLR as a further entropy source. |
56 | 152 | uint32_t stack_ptr = |
57 | 152 | static_cast<uint32_t>(reinterpret_cast<uint64_t>(&code_ptr) >> 12); |
58 | | |
59 | 152 | uint32_t entropy[] = {static_cast<uint32_t>(boot_ns >> 32), |
60 | 152 | static_cast<uint32_t>(boot_ns), |
61 | 152 | static_cast<uint32_t>(epoch_ns >> 32), |
62 | 152 | static_cast<uint32_t>(epoch_ns), |
63 | 152 | code_ptr, |
64 | 152 | stack_ptr}; |
65 | 152 | std::seed_seq entropy_seq(entropy, entropy + ArraySize(entropy)); |
66 | | |
67 | 152 | auto words = std::array<uint32_t, 4>(); |
68 | 152 | entropy_seq.generate(words.begin(), words.end()); |
69 | 152 | uint64_t msb = static_cast<uint64_t>(words[0]) << 32u | words[1]; |
70 | 152 | uint64_t lsb = static_cast<uint64_t>(words[2]) << 32u | words[3]; |
71 | 152 | return Uuid(static_cast<int64_t>(lsb), static_cast<int64_t>(msb)); |
72 | 152 | } |
73 | | |
74 | 304 | Uuid::Uuid() {} |
75 | | |
76 | 0 | Uuid::Uuid(const std::string& s) { |
77 | 0 | PERFETTO_CHECK(s.size() == data_.size()); |
78 | 0 | memcpy(data_.data(), s.data(), s.size()); |
79 | 0 | } |
80 | | |
81 | 738 | Uuid::Uuid(int64_t lsb, int64_t msb) { |
82 | 738 | set_lsb_msb(lsb, msb); |
83 | 738 | } |
84 | | |
85 | 0 | std::string Uuid::ToString() const { |
86 | 0 | return std::string(reinterpret_cast<const char*>(data_.data()), data_.size()); |
87 | 0 | } |
88 | | |
89 | 586 | std::string Uuid::ToPrettyString() const { |
90 | 586 | std::string s(data_.size() * 2 + 4, '-'); |
91 | | // Format is 123e4567-e89b-12d3-a456-426655443322. |
92 | 586 | size_t j = 0; |
93 | 9.96k | for (size_t i = 0; i < data_.size(); ++i) { |
94 | 9.37k | if (i == 4 || i == 6 || i == 8 || i == 10) |
95 | 2.34k | j++; |
96 | 9.37k | s[2 * i + j] = kHexmap[(data_[data_.size() - i - 1] & 0xf0) >> 4]; |
97 | 9.37k | s[2 * i + 1 + j] = kHexmap[(data_[data_.size() - i - 1] & 0x0f)]; |
98 | 9.37k | } |
99 | 586 | return s; |
100 | 586 | } |
101 | | |
102 | | } // namespace base |
103 | | } // namespace perfetto |