1//===-- MemoryTagManagerAArch64MTETest.cpp --------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include "Plugins/Process/Utility/MemoryTagManagerAArch64MTE.h"
10#include "llvm/Testing/Support/Error.h"
11#include "gtest/gtest.h"
12
13using namespace lldb_private;
14
15TEST(MemoryTagManagerAArch64MTETest, UnpackTagsData) {
16 MemoryTagManagerAArch64MTE manager;
17
18 // Error for insufficient tag data
19 std::vector<uint8_t> input;
20 ASSERT_THAT_EXPECTED(
21 manager.UnpackTagsData(input, 2),
22 llvm::FailedWithMessage(
23 "Packed tag data size does not match expected number of tags. "
24 "Expected 2 tag(s) for 2 granule(s), got 0 tag(s)."));
25
26 // This is out of the valid tag range
27 input.push_back(x: 0x1f);
28 ASSERT_THAT_EXPECTED(
29 manager.UnpackTagsData(input, 1),
30 llvm::FailedWithMessage(
31 "Found tag 0x1f which is > max MTE tag value of 0xf."));
32
33 // MTE tags are 1 per byte
34 input.pop_back();
35 input.push_back(x: 0xe);
36 input.push_back(x: 0xf);
37
38 std::vector<lldb::addr_t> expected{0xe, 0xf};
39
40 llvm::Expected<std::vector<lldb::addr_t>> got =
41 manager.UnpackTagsData(tags: input, granules: 2);
42 ASSERT_THAT_EXPECTED(got, llvm::Succeeded());
43 ASSERT_THAT(expected, testing::ContainerEq(*got));
44
45 // Error for too much tag data
46 ASSERT_THAT_EXPECTED(
47 manager.UnpackTagsData(input, 1),
48 llvm::FailedWithMessage(
49 "Packed tag data size does not match expected number of tags. "
50 "Expected 1 tag(s) for 1 granule(s), got 2 tag(s)."));
51
52 // By default, we don't check number of tags
53 llvm::Expected<std::vector<lldb::addr_t>> got_zero =
54 manager.UnpackTagsData(tags: input);
55 ASSERT_THAT_EXPECTED(got_zero, llvm::Succeeded());
56 ASSERT_THAT(expected, testing::ContainerEq(*got));
57
58 // Which is the same as granules=0
59 got_zero = manager.UnpackTagsData(tags: input, granules: 0);
60 ASSERT_THAT_EXPECTED(got_zero, llvm::Succeeded());
61 ASSERT_THAT(expected, testing::ContainerEq(*got));
62}
63
64TEST(MemoryTagManagerAArch64MTETest, PackTags) {
65 MemoryTagManagerAArch64MTE manager;
66
67 // Error for tag out of range
68 llvm::Expected<std::vector<uint8_t>> invalid_tag_err =
69 manager.PackTags(tags: {0x10});
70 ASSERT_THAT_EXPECTED(
71 invalid_tag_err,
72 llvm::FailedWithMessage(
73 "Found tag 0x10 which is > max MTE tag value of 0xf."));
74
75 // 0xf here is the max tag value that we can pack
76 std::vector<lldb::addr_t> tags{0, 1, 0xf};
77 std::vector<uint8_t> expected{0, 1, 0xf};
78 llvm::Expected<std::vector<uint8_t>> packed = manager.PackTags(tags);
79 ASSERT_THAT_EXPECTED(packed, llvm::Succeeded());
80 ASSERT_THAT(expected, testing::ContainerEq(*packed));
81}
82
83TEST(MemoryTagManagerAArch64MTETest, UnpackTagsFromCoreFileSegment) {
84 MemoryTagManagerAArch64MTE manager;
85 // This is our fake segment data where tags are compressed as 2 4 bit tags
86 // per byte.
87 std::vector<uint8_t> tags_data;
88 MemoryTagManager::CoreReaderFn reader =
89 [&tags_data](lldb::offset_t offset, size_t length, void *dst) {
90 std::memcpy(dest: dst, src: tags_data.data() + offset, n: length);
91 return length;
92 };
93
94 // Zero length is ok.
95 std::vector<lldb::addr_t> tags =
96 manager.UnpackTagsFromCoreFileSegment(reader, tag_segment_virtual_address: 0, tag_segment_data_address: 0, addr: 0, len: 0);
97 ASSERT_EQ(tags.size(), (size_t)0);
98
99 // In the simplest case we read 2 tags which are in the same byte.
100 tags_data.push_back(x: 0x21);
101 // The least significant bits are the first tag in memory.
102 std::vector<lldb::addr_t> expected{1, 2};
103 tags = manager.UnpackTagsFromCoreFileSegment(reader, tag_segment_virtual_address: 0, tag_segment_data_address: 0, addr: 0, len: 32);
104 ASSERT_THAT(expected, testing::ContainerEq(tags));
105
106 // If we read just one then it will have to trim off the second one.
107 expected = std::vector<lldb::addr_t>{1};
108 tags = manager.UnpackTagsFromCoreFileSegment(reader, tag_segment_virtual_address: 0, tag_segment_data_address: 0, addr: 0, len: 16);
109 ASSERT_THAT(expected, testing::ContainerEq(tags));
110
111 // If we read the second tag only then the first one must be trimmed.
112 expected = std::vector<lldb::addr_t>{2};
113 tags = manager.UnpackTagsFromCoreFileSegment(reader, tag_segment_virtual_address: 0, tag_segment_data_address: 0, addr: 16, len: 16);
114 ASSERT_THAT(expected, testing::ContainerEq(tags));
115
116 // This trimming logic applies if you read a larger set of tags.
117 tags_data = std::vector<uint8_t>{0x21, 0x43, 0x65, 0x87};
118
119 // Trailing tag should be trimmed.
120 expected = std::vector<lldb::addr_t>{1, 2, 3};
121 tags = manager.UnpackTagsFromCoreFileSegment(reader, tag_segment_virtual_address: 0, tag_segment_data_address: 0, addr: 0, len: 48);
122 ASSERT_THAT(expected, testing::ContainerEq(tags));
123
124 // Leading tag should be trimmed.
125 expected = std::vector<lldb::addr_t>{2, 3, 4};
126 tags = manager.UnpackTagsFromCoreFileSegment(reader, tag_segment_virtual_address: 0, tag_segment_data_address: 0, addr: 16, len: 48);
127 ASSERT_THAT(expected, testing::ContainerEq(tags));
128
129 // Leading and trailing trimmmed.
130 expected = std::vector<lldb::addr_t>{2, 3, 4, 5};
131 tags = manager.UnpackTagsFromCoreFileSegment(reader, tag_segment_virtual_address: 0, tag_segment_data_address: 0, addr: 16, len: 64);
132 ASSERT_THAT(expected, testing::ContainerEq(tags));
133
134 // The address given is an offset into the whole file so the address requested
135 // from the reader should be beyond that.
136 tags_data = std::vector<uint8_t>{0xFF, 0xFF, 0x21, 0x43, 0x65, 0x87};
137 expected = std::vector<lldb::addr_t>{1, 2};
138 tags = manager.UnpackTagsFromCoreFileSegment(reader, tag_segment_virtual_address: 0, tag_segment_data_address: 2, addr: 0, len: 32);
139 ASSERT_THAT(expected, testing::ContainerEq(tags));
140
141 // addr is a virtual address that we expect to be >= the tag segment's
142 // starting virtual address. So again an offset must be made from the
143 // difference.
144 expected = std::vector<lldb::addr_t>{3, 4};
145 tags = manager.UnpackTagsFromCoreFileSegment(reader, tag_segment_virtual_address: 32, tag_segment_data_address: 2, addr: 64, len: 32);
146 ASSERT_THAT(expected, testing::ContainerEq(tags));
147}
148
149TEST(MemoryTagManagerAArch64MTETest, GetLogicalTag) {
150 MemoryTagManagerAArch64MTE manager;
151
152 // Set surrounding bits to check shift is correct
153 ASSERT_EQ((lldb::addr_t)0, manager.GetLogicalTag(0xe0e00000ffffffff));
154 // Max tag value
155 ASSERT_EQ((lldb::addr_t)0xf, manager.GetLogicalTag(0x0f000000ffffffff));
156 ASSERT_EQ((lldb::addr_t)2, manager.GetLogicalTag(0x02000000ffffffff));
157}
158
159TEST(MemoryTagManagerAArch64MTETest, ExpandToGranule) {
160 MemoryTagManagerAArch64MTE manager;
161 // Reading nothing, no alignment needed
162 ASSERT_EQ(
163 MemoryTagManagerAArch64MTE::TagRange(0, 0),
164 manager.ExpandToGranule(MemoryTagManagerAArch64MTE::TagRange(0, 0)));
165
166 // Ranges with 0 size are unchanged even if address is non 0
167 // (normally 0x1234 would be aligned to 0x1230)
168 ASSERT_EQ(
169 MemoryTagManagerAArch64MTE::TagRange(0x1234, 0),
170 manager.ExpandToGranule(MemoryTagManagerAArch64MTE::TagRange(0x1234, 0)));
171
172 // Ranges already aligned don't change
173 ASSERT_EQ(
174 MemoryTagManagerAArch64MTE::TagRange(0x100, 64),
175 manager.ExpandToGranule(MemoryTagManagerAArch64MTE::TagRange(0x100, 64)));
176
177 // Any read of less than 1 granule is rounded up to reading 1 granule
178 ASSERT_EQ(
179 MemoryTagManagerAArch64MTE::TagRange(0, 16),
180 manager.ExpandToGranule(MemoryTagManagerAArch64MTE::TagRange(0, 1)));
181
182 // Start address is aligned down, and length modified accordingly
183 // Here bytes 8 through 24 straddle 2 granules. So the resulting range starts
184 // at 0 and covers 32 bytes.
185 ASSERT_EQ(
186 MemoryTagManagerAArch64MTE::TagRange(0, 32),
187 manager.ExpandToGranule(MemoryTagManagerAArch64MTE::TagRange(8, 16)));
188
189 // Here only the size of the range needs aligning
190 ASSERT_EQ(
191 MemoryTagManagerAArch64MTE::TagRange(16, 32),
192 manager.ExpandToGranule(MemoryTagManagerAArch64MTE::TagRange(16, 24)));
193
194 // Start and size need aligning here but we only need 1 granule to cover it
195 ASSERT_EQ(
196 MemoryTagManagerAArch64MTE::TagRange(16, 16),
197 manager.ExpandToGranule(MemoryTagManagerAArch64MTE::TagRange(18, 4)));
198}
199
200static MemoryRegionInfo MakeRegionInfo(lldb::addr_t base, lldb::addr_t size,
201 bool tagged) {
202 return MemoryRegionInfo(
203 MemoryRegionInfo::RangeType(base, size),
204 MemoryRegionInfo::eYes, MemoryRegionInfo::eYes, MemoryRegionInfo::eYes,
205 MemoryRegionInfo::eNo,
206 MemoryRegionInfo::eYes,
207 ConstString(), MemoryRegionInfo::eNo, 0,
208 /*memory_tagged=*/
209 tagged ? MemoryRegionInfo::eYes : MemoryRegionInfo::eNo,
210 MemoryRegionInfo::eDontKnow);
211}
212
213TEST(MemoryTagManagerAArch64MTETest, MakeTaggedRange) {
214 MemoryTagManagerAArch64MTE manager;
215 MemoryRegionInfos memory_regions;
216
217 // No regions means no tagged regions, error
218 ASSERT_THAT_EXPECTED(
219 manager.MakeTaggedRange(0, 0x10, memory_regions),
220 llvm::FailedWithMessage(
221 "Address range 0x0:0x10 is not in a memory tagged region"));
222
223 // Alignment is done before checking regions.
224 // Here 1 is rounded up to the granule size of 0x10.
225 ASSERT_THAT_EXPECTED(
226 manager.MakeTaggedRange(0, 1, memory_regions),
227 llvm::FailedWithMessage(
228 "Address range 0x0:0x10 is not in a memory tagged region"));
229
230 // Range must not be inverted
231 ASSERT_THAT_EXPECTED(
232 manager.MakeTaggedRange(1, 0, memory_regions),
233 llvm::FailedWithMessage(
234 "End address (0x0) must be greater than the start address (0x1)"));
235
236 // The inversion check ignores tags in the addresses (MTE tags start at bit
237 // 56).
238 ASSERT_THAT_EXPECTED(
239 manager.MakeTaggedRange((lldb::addr_t)1 << 56,
240 ((lldb::addr_t)2 << 56) + 0x10, memory_regions),
241 llvm::FailedWithMessage(
242 "Address range 0x0:0x10 is not in a memory tagged region"));
243
244 // Adding a single region to cover the whole range
245 memory_regions.push_back(x: MakeRegionInfo(base: 0, size: 0x1000, tagged: true));
246
247 // Range can have different tags for begin and end
248 // (which would make it look inverted if we didn't remove them)
249 // Note that range comes back with an untagged base and alginment
250 // applied.
251 MemoryTagManagerAArch64MTE::TagRange expected_range(0x0, 0x10);
252 llvm::Expected<MemoryTagManagerAArch64MTE::TagRange> got =
253 manager.MakeTaggedRange(addr: 0x0f00000000000000, end_addr: 0x0e00000000000001,
254 memory_regions);
255 ASSERT_THAT_EXPECTED(got, llvm::Succeeded());
256 ASSERT_EQ(*got, expected_range);
257
258 // Error if the range isn't within any region
259 ASSERT_THAT_EXPECTED(
260 manager.MakeTaggedRange(0x1000, 0x1010, memory_regions),
261 llvm::FailedWithMessage(
262 "Address range 0x1000:0x1010 is not in a memory tagged region"));
263
264 // Error if the first part of a range isn't tagged
265 memory_regions.clear();
266 const char *err_msg =
267 "Address range 0x0:0x1000 is not in a memory tagged region";
268
269 // First because it has no region entry
270 memory_regions.push_back(x: MakeRegionInfo(base: 0x10, size: 0x1000, tagged: true));
271 ASSERT_THAT_EXPECTED(manager.MakeTaggedRange(0, 0x1000, memory_regions),
272 llvm::FailedWithMessage(err_msg));
273
274 // Then because the first region is untagged
275 memory_regions.push_back(x: MakeRegionInfo(base: 0, size: 0x10, tagged: false));
276 ASSERT_THAT_EXPECTED(manager.MakeTaggedRange(0, 0x1000, memory_regions),
277 llvm::FailedWithMessage(err_msg));
278
279 // If we tag that first part it succeeds
280 memory_regions.back().SetMemoryTagged(MemoryRegionInfo::eYes);
281 expected_range = MemoryTagManagerAArch64MTE::TagRange(0x0, 0x1000);
282 got = manager.MakeTaggedRange(addr: 0, end_addr: 0x1000, memory_regions);
283 ASSERT_THAT_EXPECTED(got, llvm::Succeeded());
284 ASSERT_EQ(*got, expected_range);
285
286 // Error if the end of a range is untagged
287 memory_regions.clear();
288
289 // First because it has no region entry
290 memory_regions.push_back(x: MakeRegionInfo(base: 0, size: 0xF00, tagged: true));
291 ASSERT_THAT_EXPECTED(manager.MakeTaggedRange(0, 0x1000, memory_regions),
292 llvm::FailedWithMessage(err_msg));
293
294 // Then because the last region is untagged
295 memory_regions.push_back(x: MakeRegionInfo(base: 0xF00, size: 0x100, tagged: false));
296 ASSERT_THAT_EXPECTED(manager.MakeTaggedRange(0, 0x1000, memory_regions),
297 llvm::FailedWithMessage(err_msg));
298
299 // If we tag the last part it succeeds
300 memory_regions.back().SetMemoryTagged(MemoryRegionInfo::eYes);
301 got = manager.MakeTaggedRange(addr: 0, end_addr: 0x1000, memory_regions);
302 ASSERT_THAT_EXPECTED(got, llvm::Succeeded());
303 ASSERT_EQ(*got, expected_range);
304
305 // Error if the middle of a range is untagged
306 memory_regions.clear();
307
308 // First because it has no entry
309 memory_regions.push_back(x: MakeRegionInfo(base: 0, size: 0x500, tagged: true));
310 memory_regions.push_back(x: MakeRegionInfo(base: 0x900, size: 0x700, tagged: true));
311 ASSERT_THAT_EXPECTED(manager.MakeTaggedRange(0, 0x1000, memory_regions),
312 llvm::FailedWithMessage(err_msg));
313
314 // Then because it's untagged
315 memory_regions.push_back(x: MakeRegionInfo(base: 0x500, size: 0x400, tagged: false));
316 ASSERT_THAT_EXPECTED(manager.MakeTaggedRange(0, 0x1000, memory_regions),
317 llvm::FailedWithMessage(err_msg));
318
319 // If we tag the middle part it succeeds
320 memory_regions.back().SetMemoryTagged(MemoryRegionInfo::eYes);
321 got = manager.MakeTaggedRange(addr: 0, end_addr: 0x1000, memory_regions);
322 ASSERT_THAT_EXPECTED(got, llvm::Succeeded());
323 ASSERT_EQ(*got, expected_range);
324}
325
326TEST(MemoryTagManagerAArch64MTETest, MakeTaggedRanges) {
327 MemoryTagManagerAArch64MTE manager;
328 MemoryRegionInfos memory_regions;
329
330 // Note that MakeTaggedRanges takes start/end address.
331 // Whereas TagRanges and regions take start address and size.
332
333 // Range must not be inverted
334 ASSERT_THAT_EXPECTED(
335 manager.MakeTaggedRanges(1, 0, memory_regions),
336 llvm::FailedWithMessage(
337 "End address (0x0) must be greater than the start address (0x1)"));
338
339 // We remove tags before doing the inversion check, so this is not an error.
340 // Also no regions means no tagged regions returned.
341 // (bit 56 is where MTE tags begin)
342 llvm::Expected<std::vector<MemoryTagManager::TagRange>> got =
343 manager.MakeTaggedRanges(addr: (lldb::addr_t)2 << 56,
344 end_addr: ((lldb::addr_t)1 << 56) + 0x10, memory_regions);
345 ASSERT_THAT_EXPECTED(got, llvm::Succeeded());
346 ASSERT_EQ(*got, std::vector<MemoryTagManager::TagRange>{});
347
348 // Cover whole range, untagged. No ranges returned.
349 memory_regions.push_back(x: MakeRegionInfo(base: 0, size: 0x20, tagged: false));
350 got = manager.MakeTaggedRanges(addr: 0, end_addr: 0x20, memory_regions);
351 ASSERT_THAT_EXPECTED(got, llvm::Succeeded());
352 ASSERT_EQ(*got, std::vector<MemoryTagManager::TagRange>{});
353
354 // Make the region tagged and it'll be the one range returned.
355 memory_regions.back().SetMemoryTagged(MemoryRegionInfo::eYes);
356 got = manager.MakeTaggedRanges(addr: 0, end_addr: 0x20, memory_regions);
357 ASSERT_THAT_EXPECTED(got, llvm::Succeeded());
358 ASSERT_EQ(*got, std::vector<MemoryTagManager::TagRange>{
359 MemoryTagManager::TagRange(0, 0x20)});
360
361 // This region will be trimmed if it's larger than the whole range.
362 memory_regions.clear();
363 memory_regions.push_back(x: MakeRegionInfo(base: 0, size: 0x40, tagged: true));
364 got = manager.MakeTaggedRanges(addr: 0x10, end_addr: 0x30, memory_regions);
365 ASSERT_THAT_EXPECTED(got, llvm::Succeeded());
366 ASSERT_EQ(*got, std::vector<MemoryTagManager::TagRange>{
367 MemoryTagManager::TagRange(0x10, 0x20)});
368
369 memory_regions.clear();
370
371 // For the following tests we keep the input regions
372 // in ascending order as MakeTaggedRanges expects.
373
374 // Only start of range is tagged, only that is returned.
375 // Start the region just before the requested range to check
376 // we limit the result to the requested range.
377 memory_regions.push_back(x: MakeRegionInfo(base: 0, size: 0x20, tagged: true));
378 got = manager.MakeTaggedRanges(addr: 0x10, end_addr: 0x100, memory_regions);
379 ASSERT_THAT_EXPECTED(got, llvm::Succeeded());
380 ASSERT_EQ(*got, std::vector<MemoryTagManager::TagRange>{
381 MemoryTagManager::TagRange(0x10, 0x10)});
382
383 // Add a tagged region at the end, now we get both
384 // and the middle is untagged.
385 // <tagged: [0x0, 0x20)>
386 // <...>
387 // <tagged: [0xE0, 0x120)>
388 // The range added here is deliberately over the end of the
389 // requested range to show that we trim the end.
390 memory_regions.push_back(x: MakeRegionInfo(base: 0xE0, size: 0x40, tagged: true));
391 got = manager.MakeTaggedRanges(addr: 0x10, end_addr: 0x110, memory_regions);
392 ASSERT_THAT_EXPECTED(got, llvm::Succeeded());
393
394 std::vector<MemoryTagManager::TagRange> expected{
395 MemoryTagManager::TagRange(0x10, 0x10),
396 MemoryTagManager::TagRange(0xE0, 0x30)};
397 ASSERT_EQ(*got, expected);
398
399 // Now add a middle tagged region.
400 // <tagged: [0x0, 0x20)>
401 // <...>
402 // <tagged: [0x90, 0xB0)>
403 // <...>
404 // <tagged: [0xE0, 0x120)>
405 memory_regions.insert(position: std::next(x: memory_regions.begin()),
406 x: MakeRegionInfo(base: 0x90, size: 0x20, tagged: true));
407
408 // As the given regions are in ascending order, the resulting
409 // tagged ranges are also. So this new range goes in the middle.
410 expected.insert(position: std::next(x: expected.begin()),
411 x: MemoryTagManager::TagRange(0x90, 0x20));
412 got = manager.MakeTaggedRanges(addr: 0x10, end_addr: 0x110, memory_regions);
413 ASSERT_THAT_EXPECTED(got, llvm::Succeeded());
414 ASSERT_EQ(*got, expected);
415
416 // Then if we add untagged regions in between the tagged,
417 // the output should stay the same.
418 // <tagged: [0x0, 0x20)>
419 // <untagged: [0x20, 0x90)>
420 // <tagged: [0x90, 0xB0)>
421 // <untagged: [0xB0, 0xE0)>
422 // <tagged: [0xE0, 0x120)>
423 memory_regions.insert(position: std::next(x: memory_regions.begin()),
424 x: MakeRegionInfo(base: 0x20, size: 0x70, tagged: false));
425 memory_regions.insert(position: std::prev(x: memory_regions.end()),
426 x: MakeRegionInfo(base: 0xB0, size: 0x30, tagged: false));
427 got = manager.MakeTaggedRanges(addr: 0x10, end_addr: 0x110, memory_regions);
428 ASSERT_THAT_EXPECTED(got, llvm::Succeeded());
429 ASSERT_EQ(*got, expected);
430
431 // Finally check that we handle only having the end of the range.
432 memory_regions.clear();
433 expected.clear();
434
435 memory_regions.push_back(x: MakeRegionInfo(base: 0x100, size: 0x10, tagged: true));
436 expected.push_back(x: MemoryTagManager::TagRange(0x100, 0x10));
437 got = manager.MakeTaggedRanges(addr: 0x10, end_addr: 0x110, memory_regions);
438 ASSERT_THAT_EXPECTED(got, llvm::Succeeded());
439 ASSERT_EQ(*got, expected);
440}
441
442TEST(MemoryTagManagerAArch64MTETest, RemoveTagBits) {
443 MemoryTagManagerAArch64MTE manager;
444
445 ASSERT_EQ(0, 0);
446 // Removes the whole top byte
447 ASSERT_EQ((lldb::addr_t)0x00ffeedd11223344,
448 manager.RemoveTagBits(0x00ffeedd11223344));
449 ASSERT_EQ((lldb::addr_t)0x0000000000000000,
450 manager.RemoveTagBits(0xff00000000000000));
451 ASSERT_EQ((lldb::addr_t)0x0055555566666666,
452 manager.RemoveTagBits(0xee55555566666666));
453}
454
455TEST(MemoryTagManagerAArch64MTETest, AddressDiff) {
456 MemoryTagManagerAArch64MTE manager;
457
458 ASSERT_EQ(0, manager.AddressDiff(0, 0));
459 // Result is signed
460 ASSERT_EQ(10, manager.AddressDiff(10, 0));
461 ASSERT_EQ(-10, manager.AddressDiff(0, 10));
462 // Anything in the top byte is ignored
463 ASSERT_EQ(0, manager.AddressDiff(0x2211222233334444, 0x3311222233334444));
464 ASSERT_EQ(-32, manager.AddressDiff(0x5511222233334400, 0x4411222233334420));
465 ASSERT_EQ(65, manager.AddressDiff(0x9911222233334441, 0x6611222233334400));
466}
467
468// Helper to check that repeating "tags" over "range" gives you
469// "expected_tags".
470static void
471test_repeating_tags(const std::vector<lldb::addr_t> &tags,
472 MemoryTagManagerAArch64MTE::TagRange range,
473 const std::vector<lldb::addr_t> &expected_tags) {
474 MemoryTagManagerAArch64MTE manager;
475 llvm::Expected<std::vector<lldb::addr_t>> tags_or_err =
476 manager.RepeatTagsForRange(tags, range);
477 ASSERT_THAT_EXPECTED(tags_or_err, llvm::Succeeded());
478 ASSERT_THAT(expected_tags, testing::ContainerEq(*tags_or_err));
479}
480
481TEST(MemoryTagManagerAArch64MTETest, RepeatTagsForRange) {
482 MemoryTagManagerAArch64MTE manager;
483
484 // Must have some tags if your range is not empty
485 llvm::Expected<std::vector<lldb::addr_t>> no_tags_err =
486 manager.RepeatTagsForRange(tags: {},
487 range: MemoryTagManagerAArch64MTE::TagRange{0, 16});
488 ASSERT_THAT_EXPECTED(
489 no_tags_err, llvm::FailedWithMessage(
490 "Expected some tags to cover given range, got zero."));
491
492 // If the range is empty, you get no tags back
493 test_repeating_tags(tags: {1, 2, 3}, range: MemoryTagManagerAArch64MTE::TagRange{0, 0},
494 expected_tags: {});
495 // And you don't need tags for an empty range
496 test_repeating_tags(tags: {}, range: MemoryTagManagerAArch64MTE::TagRange{0, 0}, expected_tags: {});
497
498 // A single tag will just be multiplied as many times as needed
499 test_repeating_tags(tags: {5}, range: MemoryTagManagerAArch64MTE::TagRange{0, 16}, expected_tags: {5});
500 test_repeating_tags(tags: {6}, range: MemoryTagManagerAArch64MTE::TagRange{0, 32}, expected_tags: {6, 6});
501
502 // If you've got as many tags as granules, it's a roundtrip
503 test_repeating_tags(tags: {7, 8}, range: MemoryTagManagerAArch64MTE::TagRange{0, 32},
504 expected_tags: {7, 8});
505
506 // If you've got fewer tags than granules, they repeat. Exactly or partially
507 // as needed.
508 test_repeating_tags(tags: {7, 8}, range: MemoryTagManagerAArch64MTE::TagRange{0, 64},
509 expected_tags: {7, 8, 7, 8});
510 test_repeating_tags(tags: {7, 8}, range: MemoryTagManagerAArch64MTE::TagRange{0, 48},
511 expected_tags: {7, 8, 7});
512
513 // If you've got more tags than granules you get back only those needed
514 test_repeating_tags(tags: {1, 2, 3, 4}, range: MemoryTagManagerAArch64MTE::TagRange{0, 32},
515 expected_tags: {1, 2});
516}
517

source code of lldb/unittests/Process/Utility/MemoryTagManagerAArch64MTETest.cpp