Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/tools/unittests/test_cmatch.py
170833 views
1
#!/usr/bin/env python3
2
# SPDX-License-Identifier: GPL-2.0
3
# Copyright(c) 2026: Mauro Carvalho Chehab <[email protected]>.
4
#
5
# pylint: disable=C0413,R0904
6
7
8
"""
9
Unit tests for kernel-doc CMatch.
10
"""
11
12
import os
13
import re
14
import sys
15
import unittest
16
17
18
# Import Python modules
19
20
SRC_DIR = os.path.dirname(os.path.realpath(__file__))
21
sys.path.insert(0, os.path.join(SRC_DIR, "../lib/python"))
22
23
from kdoc.c_lex import CMatch
24
from kdoc.kdoc_re import KernRe
25
from unittest_helper import run_unittest
26
27
#
28
# Override unittest.TestCase to better compare diffs ignoring whitespaces
29
#
30
class TestCaseDiff(unittest.TestCase):
31
"""
32
Disable maximum limit on diffs and add a method to better
33
handle diffs with whitespace differences.
34
"""
35
36
@classmethod
37
def setUpClass(cls):
38
"""Ensure that there won't be limit for diffs"""
39
cls.maxDiff = None
40
41
42
#
43
# Tests doing with different macros
44
#
45
46
class TestSearch(TestCaseDiff):
47
"""
48
Test search mechanism
49
"""
50
51
def test_search_acquires_simple(self):
52
line = "__acquires(ctx) foo();"
53
result = ", ".join(CMatch("__acquires").search(line))
54
self.assertEqual(result, "__acquires(ctx)")
55
56
def test_search_acquires_multiple(self):
57
line = "__acquires(ctx) __acquires(other) bar();"
58
result = ", ".join(CMatch("__acquires").search(line))
59
self.assertEqual(result, "__acquires(ctx), __acquires(other)")
60
61
def test_search_acquires_nested_paren(self):
62
line = "__acquires((ctx1, ctx2)) baz();"
63
result = ", ".join(CMatch("__acquires").search(line))
64
self.assertEqual(result, "__acquires((ctx1, ctx2))")
65
66
def test_search_must_hold(self):
67
line = "__must_hold(&lock) do_something();"
68
result = ", ".join(CMatch("__must_hold").search(line))
69
self.assertEqual(result, "__must_hold(&lock)")
70
71
def test_search_must_hold_shared(self):
72
line = "__must_hold_shared(RCU) other();"
73
result = ", ".join(CMatch("__must_hold_shared").search(line))
74
self.assertEqual(result, "__must_hold_shared(RCU)")
75
76
def test_search_no_false_positive(self):
77
line = "call__acquires(foo); // should stay intact"
78
result = ", ".join(CMatch(r"__acquires").search(line))
79
self.assertEqual(result, "")
80
81
def test_search_no_macro_remains(self):
82
line = "do_something_else();"
83
result = ", ".join(CMatch("__acquires").search(line))
84
self.assertEqual(result, "")
85
86
def test_search_no_function(self):
87
line = "something"
88
result = ", ".join(CMatch(line).search(line))
89
self.assertEqual(result, "")
90
91
#
92
# Override unittest.TestCase to better compare diffs ignoring whitespaces
93
#
94
class TestCaseDiff(unittest.TestCase):
95
"""
96
Disable maximum limit on diffs and add a method to better
97
handle diffs with whitespace differences.
98
"""
99
100
@classmethod
101
def setUpClass(cls):
102
"""Ensure that there won't be limit for diffs"""
103
cls.maxDiff = None
104
105
def assertLogicallyEqual(self, a, b):
106
"""
107
Compare two results ignoring multiple whitespace differences.
108
109
This is useful to check more complex matches picked from examples.
110
On a plus side, we also don't need to use dedent.
111
Please notice that line breaks still need to match. We might
112
remove it at the regex, but this way, checking the diff is easier.
113
"""
114
a = re.sub(r"[\t ]+", " ", a.strip())
115
b = re.sub(r"[\t ]+", " ", b.strip())
116
117
a = re.sub(r"\s+\n", "\n", a)
118
b = re.sub(r"\s+\n", "\n", b)
119
120
a = re.sub(" ;", ";", a)
121
b = re.sub(" ;", ";", b)
122
123
self.assertEqual(a, b)
124
125
#
126
# Tests doing with different macros
127
#
128
129
class TestSubMultipleMacros(TestCaseDiff):
130
"""
131
Tests doing with different macros.
132
133
Here, we won't use assertLogicallyEqual. Instead, we'll check if each
134
of the expected patterns are present at the answer.
135
"""
136
137
def test_acquires_simple(self):
138
"""Simple replacement test with __acquires"""
139
line = "__acquires(ctx) foo();"
140
result = CMatch(r"__acquires").sub("REPLACED", line)
141
142
self.assertEqual("REPLACED foo();", result)
143
144
def test_acquires_multiple(self):
145
"""Multiple __acquires"""
146
line = "__acquires(ctx) __acquires(other) bar();"
147
result = CMatch(r"__acquires").sub("REPLACED", line)
148
149
self.assertEqual("REPLACED REPLACED bar();", result)
150
151
def test_acquires_nested_paren(self):
152
"""__acquires with nested pattern"""
153
line = "__acquires((ctx1, ctx2)) baz();"
154
result = CMatch(r"__acquires").sub("REPLACED", line)
155
156
self.assertEqual("REPLACED baz();", result)
157
158
def test_must_hold(self):
159
"""__must_hold with a pointer"""
160
line = "__must_hold(&lock) do_something();"
161
result = CMatch(r"__must_hold").sub("REPLACED", line)
162
163
self.assertNotIn("__must_hold(", result)
164
self.assertIn("do_something();", result)
165
166
def test_must_hold_shared(self):
167
"""__must_hold with an upercase defined value"""
168
line = "__must_hold_shared(RCU) other();"
169
result = CMatch(r"__must_hold_shared").sub("REPLACED", line)
170
171
self.assertNotIn("__must_hold_shared(", result)
172
self.assertIn("other();", result)
173
174
def test_no_false_positive(self):
175
"""
176
Ensure that unrelated text containing similar patterns is preserved
177
"""
178
line = "call__acquires(foo); // should stay intact"
179
result = CMatch(r"\b__acquires").sub("REPLACED", line)
180
181
self.assertLogicallyEqual(result, "call__acquires(foo);")
182
183
def test_mixed_macros(self):
184
"""Add a mix of macros"""
185
line = "__acquires(ctx) __releases(ctx) __must_hold(&lock) foo();"
186
187
result = CMatch(r"__acquires").sub("REPLACED", line)
188
result = CMatch(r"__releases").sub("REPLACED", result)
189
result = CMatch(r"__must_hold").sub("REPLACED", result)
190
191
self.assertNotIn("__acquires(", result)
192
self.assertNotIn("__releases(", result)
193
self.assertNotIn("__must_hold(", result)
194
195
self.assertIn("foo();", result)
196
197
def test_no_macro_remains(self):
198
"""Ensures that unmatched macros are untouched"""
199
line = "do_something_else();"
200
result = CMatch(r"__acquires").sub("REPLACED", line)
201
202
self.assertEqual(result, line)
203
204
def test_no_function(self):
205
"""Ensures that no functions will remain untouched"""
206
line = "something"
207
result = CMatch(line).sub("REPLACED", line)
208
209
self.assertEqual(result, line)
210
211
#
212
# Check if the diff is logically equivalent. To simplify, the tests here
213
# use a single macro name for all replacements.
214
#
215
216
class TestSubSimple(TestCaseDiff):
217
"""
218
Test argument replacements.
219
220
Here, the function name can be anything. So, we picked __attribute__(),
221
to mimic a macro found at the Kernel, but none of the replacements her
222
has any relationship with the Kernel usage.
223
"""
224
225
MACRO = "__attribute__"
226
227
@classmethod
228
def setUpClass(cls):
229
"""Define a CMatch to be used for all tests"""
230
cls.matcher = CMatch(cls.MACRO)
231
232
def test_sub_with_capture(self):
233
"""Test all arguments replacement with a single arg"""
234
line = f"{self.MACRO}(&ctx)\nfoo();"
235
236
result = self.matcher.sub(r"ACQUIRED(\0)", line)
237
238
self.assertLogicallyEqual("ACQUIRED(&ctx)\nfoo();", result)
239
240
def test_sub_zero_placeholder(self):
241
"""Test all arguments replacement with a multiple args"""
242
line = f"{self.MACRO}(arg1, arg2)\nbar();"
243
244
result = self.matcher.sub(r"REPLACED(\0)", line)
245
246
self.assertLogicallyEqual("REPLACED(arg1, arg2)\nbar();", result)
247
248
def test_sub_single_placeholder(self):
249
"""Single replacement rule for \1"""
250
line = f"{self.MACRO}(ctx, boo)\nfoo();"
251
result = self.matcher.sub(r"ACQUIRED(\1)", line)
252
253
self.assertLogicallyEqual("ACQUIRED(ctx)\nfoo();", result)
254
255
def test_sub_multiple_placeholders(self):
256
"""Replacement rule for both \1 and \2"""
257
line = f"{self.MACRO}(arg1, arg2)\nbar();"
258
result = self.matcher.sub(r"REPLACE(\1, \2)", line)
259
260
self.assertLogicallyEqual("REPLACE(arg1, arg2)\nbar();", result)
261
262
def test_sub_mixed_placeholders(self):
263
"""Replacement rule for \0, \1 and additional text"""
264
line = f"{self.MACRO}(foo, bar)\nbaz();"
265
result = self.matcher.sub(r"ALL(\0) FIRST(\1)", line)
266
267
self.assertLogicallyEqual("ALL(foo, bar) FIRST(foo)\nbaz();", result)
268
269
def test_sub_no_placeholder(self):
270
"""Replacement without placeholders"""
271
line = f"{self.MACRO}(arg)\nfoo();"
272
result = self.matcher.sub(r"NO_BACKREFS()", line)
273
274
self.assertLogicallyEqual("NO_BACKREFS()\nfoo();", result)
275
276
def test_sub_count_parameter(self):
277
"""Verify that the algorithm stops after the requested count"""
278
line = f"{self.MACRO}(a1) x();\n{self.MACRO}(a2) y();"
279
result = self.matcher.sub(r"ONLY_FIRST(\1) ", line, count=1)
280
281
self.assertLogicallyEqual(f"ONLY_FIRST(a1) x();\n{self.MACRO}(a2) y();",
282
result)
283
284
def test_strip_multiple_acquires(self):
285
"""Check if spaces between removed delimiters will be dropped"""
286
line = f"int {self.MACRO}(1) {self.MACRO}(2 ) {self.MACRO}(3) foo;"
287
result = self.matcher.sub("", line)
288
289
self.assertLogicallyEqual(result, "int foo;")
290
291
def test_rise_early_greedy(self):
292
line = f"{self.MACRO}(a, b, c, d);"
293
sub = r"\1, \2+, \3"
294
295
with self.assertRaises(ValueError):
296
result = self.matcher.sub(sub, line)
297
298
def test_rise_multiple_greedy(self):
299
line = f"{self.MACRO}(a, b, c, d);"
300
sub = r"\1, \2+, \3+"
301
302
with self.assertRaises(ValueError):
303
result = self.matcher.sub(sub, line)
304
305
#
306
# Test replacements with slashrefs
307
#
308
309
310
class TestSubWithLocalXforms(TestCaseDiff):
311
"""
312
Test diferent usecase patterns found at the Kernel.
313
314
Here, replacements using both CMatch and KernRe can be tested,
315
as it will import the actual replacement rules used by kernel-doc.
316
"""
317
318
struct_xforms = [
319
(CMatch("__attribute__"), ' '),
320
(CMatch('__aligned'), ' '),
321
(CMatch('__counted_by'), ' '),
322
(CMatch('__counted_by_(le|be)'), ' '),
323
(CMatch('__guarded_by'), ' '),
324
(CMatch('__pt_guarded_by'), ' '),
325
326
(CMatch('__cacheline_group_(begin|end)'), ''),
327
328
(CMatch('struct_group'), r'\2'),
329
(CMatch('struct_group_attr'), r'\3'),
330
(CMatch('struct_group_tagged'), r'struct \1 { \3+ } \2;'),
331
(CMatch('__struct_group'), r'\4'),
332
333
(CMatch('__ETHTOOL_DECLARE_LINK_MODE_MASK'), r'DECLARE_BITMAP(\1, __ETHTOOL_LINK_MODE_MASK_NBITS)'),
334
(CMatch('DECLARE_PHY_INTERFACE_MASK',), r'DECLARE_BITMAP(\1, PHY_INTERFACE_MODE_MAX)'),
335
(CMatch('DECLARE_BITMAP'), r'unsigned long \1[BITS_TO_LONGS(\2)]'),
336
337
(CMatch('DECLARE_HASHTABLE'), r'unsigned long \1[1 << ((\2) - 1)]'),
338
(CMatch('DECLARE_KFIFO'), r'\2 *\1'),
339
(CMatch('DECLARE_KFIFO_PTR'), r'\2 *\1'),
340
(CMatch('(?:__)?DECLARE_FLEX_ARRAY'), r'\1 \2[]'),
341
(CMatch('DEFINE_DMA_UNMAP_ADDR'), r'dma_addr_t \1'),
342
(CMatch('DEFINE_DMA_UNMAP_LEN'), r'__u32 \1'),
343
(CMatch('VIRTIO_DECLARE_FEATURES'), r'union { u64 \1; u64 \1_array[VIRTIO_FEATURES_U64S]; }'),
344
]
345
346
function_xforms = [
347
(CMatch('__printf'), ""),
348
(CMatch('__(?:re)?alloc_size'), ""),
349
(CMatch("__diagnose_as"), ""),
350
(CMatch("DECL_BUCKET_PARAMS"), r"\1, \2"),
351
352
(CMatch("__cond_acquires"), ""),
353
(CMatch("__cond_releases"), ""),
354
(CMatch("__acquires"), ""),
355
(CMatch("__releases"), ""),
356
(CMatch("__must_hold"), ""),
357
(CMatch("__must_not_hold"), ""),
358
(CMatch("__must_hold_shared"), ""),
359
(CMatch("__cond_acquires_shared"), ""),
360
(CMatch("__acquires_shared"), ""),
361
(CMatch("__releases_shared"), ""),
362
(CMatch("__attribute__"), ""),
363
]
364
365
var_xforms = [
366
(CMatch('__guarded_by'), ""),
367
(CMatch('__pt_guarded_by'), ""),
368
(CMatch("LIST_HEAD"), r"struct list_head \1"),
369
]
370
371
#: Transforms main dictionary used at apply_transforms().
372
xforms = {
373
"struct": struct_xforms,
374
"func": function_xforms,
375
"var": var_xforms,
376
}
377
378
@classmethod
379
def apply_transforms(cls, xform_type, text):
380
"""
381
Mimic the behavior of kdoc_parser.apply_transforms() method.
382
383
For each element of STRUCT_XFORMS, apply apply_transforms.
384
385
There are two parameters:
386
387
- ``xform_type``
388
Can be ``func``, ``struct`` or ``var``;
389
- ``text``
390
The text where the sub patterns from CTransforms will be applied.
391
"""
392
for search, subst in cls.xforms.get(xform_type):
393
text = search.sub(subst, text)
394
395
return text.strip()
396
397
cls.matcher = CMatch(r"struct_group[\w\_]*")
398
399
def test_struct_group(self):
400
"""
401
Test struct_group using a pattern from
402
drivers/net/ethernet/asix/ax88796c_main.h.
403
"""
404
line = """
405
struct tx_pkt_info {
406
struct_group(tx_overhead,
407
struct tx_sop_header sop;
408
struct tx_segment_header seg;
409
);
410
struct tx_eop_header eop;
411
u16 pkt_len;
412
u16 seq_num;
413
};
414
"""
415
expected = """
416
struct tx_pkt_info {
417
struct tx_sop_header sop;
418
struct tx_segment_header seg;
419
struct tx_eop_header eop;
420
u16 pkt_len;
421
u16 seq_num;
422
};
423
"""
424
425
result = self.apply_transforms("struct", line)
426
self.assertLogicallyEqual(result, expected)
427
428
def test_struct_group_attr(self):
429
"""
430
Test two struct_group_attr using patterns from fs/smb/client/cifspdu.h.
431
"""
432
line = """
433
typedef struct smb_com_open_rsp {
434
struct smb_hdr hdr; /* wct = 34 BB */
435
__u8 AndXCommand;
436
__u8 AndXReserved;
437
__le16 AndXOffset;
438
__u8 OplockLevel;
439
__u16 Fid;
440
__le32 CreateAction;
441
struct_group_attr(common_attributes,,
442
__le64 CreationTime;
443
__le64 LastAccessTime;
444
__le64 LastWriteTime;
445
__le64 ChangeTime;
446
__le32 FileAttributes;
447
);
448
__le64 AllocationSize;
449
__le64 EndOfFile;
450
__le16 FileType;
451
__le16 DeviceState;
452
__u8 DirectoryFlag;
453
__u16 ByteCount; /* bct = 0 */
454
} OPEN_RSP;
455
typedef struct {
456
struct_group_attr(common_attributes,,
457
__le64 CreationTime;
458
__le64 LastAccessTime;
459
__le64 LastWriteTime;
460
__le64 ChangeTime;
461
__le32 Attributes;
462
);
463
__u32 Pad1;
464
__le64 AllocationSize;
465
__le64 EndOfFile;
466
__le32 NumberOfLinks;
467
__u8 DeletePending;
468
__u8 Directory;
469
__u16 Pad2;
470
__le32 EASize;
471
__le32 FileNameLength;
472
union {
473
char __pad;
474
DECLARE_FLEX_ARRAY(char, FileName);
475
};
476
} FILE_ALL_INFO; /* level 0x107 QPathInfo */
477
"""
478
expected = """
479
typedef struct smb_com_open_rsp {
480
struct smb_hdr hdr;
481
__u8 AndXCommand;
482
__u8 AndXReserved;
483
__le16 AndXOffset;
484
__u8 OplockLevel;
485
__u16 Fid;
486
__le32 CreateAction;
487
__le64 CreationTime;
488
__le64 LastAccessTime;
489
__le64 LastWriteTime;
490
__le64 ChangeTime;
491
__le32 FileAttributes;
492
__le64 AllocationSize;
493
__le64 EndOfFile;
494
__le16 FileType;
495
__le16 DeviceState;
496
__u8 DirectoryFlag;
497
__u16 ByteCount;
498
} OPEN_RSP;
499
typedef struct {
500
__le64 CreationTime;
501
__le64 LastAccessTime;
502
__le64 LastWriteTime;
503
__le64 ChangeTime;
504
__le32 Attributes;
505
__u32 Pad1;
506
__le64 AllocationSize;
507
__le64 EndOfFile;
508
__le32 NumberOfLinks;
509
__u8 DeletePending;
510
__u8 Directory;
511
__u16 Pad2;
512
__le32 EASize;
513
__le32 FileNameLength;
514
union {
515
char __pad;
516
char FileName[];
517
};
518
} FILE_ALL_INFO;
519
"""
520
521
result = self.apply_transforms("struct", line)
522
self.assertLogicallyEqual(result, expected)
523
524
def test_raw_struct_group(self):
525
"""
526
Test a __struct_group pattern from include/uapi/cxl/features.h.
527
"""
528
line = """
529
struct cxl_mbox_get_sup_feats_out {
530
__struct_group(cxl_mbox_get_sup_feats_out_hdr, hdr, /* empty */,
531
__le16 num_entries;
532
__le16 supported_feats;
533
__u8 reserved[4];
534
);
535
struct cxl_feat_entry ents[] __counted_by_le(num_entries);
536
} __attribute__ ((__packed__));
537
"""
538
expected = """
539
struct cxl_mbox_get_sup_feats_out {
540
__le16 num_entries;
541
__le16 supported_feats;
542
__u8 reserved[4];
543
struct cxl_feat_entry ents[];
544
};
545
"""
546
547
result = self.apply_transforms("struct", line)
548
self.assertLogicallyEqual(result, expected)
549
550
def test_raw_struct_group_tagged(self):
551
r"""
552
Test cxl_regs with struct_group_tagged patterns from drivers/cxl/cxl.h.
553
554
NOTE:
555
556
This one has actually a violation from what kernel-doc would
557
expect: Kernel-doc regex expects only 3 members, but this is
558
actually defined as::
559
560
#define struct_group_tagged(TAG, NAME, MEMBERS...)
561
562
The replace expression there is::
563
564
struct \1 { \3 } \2;
565
566
but it should be really something like::
567
568
struct \1 { \3 \4 \5 \6 \7 \8 ... } \2;
569
570
a later fix would be needed to address it.
571
572
"""
573
line = """
574
struct cxl_regs {
575
struct_group_tagged(cxl_component_regs, component,
576
void __iomem *hdm_decoder;
577
void __iomem *ras;
578
);
579
580
581
/* This is actually a violation: too much commas */
582
struct_group_tagged(cxl_device_regs, device_regs,
583
void __iomem *status, *mbox, *memdev;
584
);
585
586
struct_group_tagged(cxl_pmu_regs, pmu_regs,
587
void __iomem *pmu;
588
);
589
590
struct_group_tagged(cxl_rch_regs, rch_regs,
591
void __iomem *dport_aer;
592
);
593
594
struct_group_tagged(cxl_rcd_regs, rcd_regs,
595
void __iomem *rcd_pcie_cap;
596
);
597
};
598
"""
599
expected = """
600
struct cxl_regs {
601
struct cxl_component_regs {
602
void __iomem *hdm_decoder;
603
void __iomem *ras;
604
} component;
605
606
struct cxl_device_regs {
607
void __iomem *status, *mbox, *memdev;
608
} device_regs;
609
610
struct cxl_pmu_regs {
611
void __iomem *pmu;
612
} pmu_regs;
613
614
struct cxl_rch_regs {
615
void __iomem *dport_aer;
616
} rch_regs;
617
618
struct cxl_rcd_regs {
619
void __iomem *rcd_pcie_cap;
620
} rcd_regs;
621
};
622
"""
623
624
result = self.apply_transforms("struct", line)
625
self.assertLogicallyEqual(result, expected)
626
627
def test_struct_group_tagged_with_private(self):
628
"""
629
Replace struct_group_tagged with private, using the same regex
630
for the replacement as what happens in xforms_lists.py.
631
632
As the private removal happens outside NestedGroup class, we manually
633
dropped the remaining part of the struct, to simulate what happens
634
at kdoc_parser.
635
636
Taken from include/net/page_pool/types.h
637
"""
638
line = """
639
struct page_pool_params {
640
struct_group_tagged(page_pool_params_slow, slow,
641
struct net_device *netdev;
642
unsigned int queue_idx;
643
unsigned int flags;
644
/* private: only under "slow" struct */
645
unsigned int ignored;
646
);
647
/* Struct below shall not be ignored */
648
struct_group_tagged(page_pool_params_fast, fast,
649
unsigned int order;
650
unsigned int pool_size;
651
int nid;
652
struct device *dev;
653
struct napi_struct *napi;
654
enum dma_data_direction dma_dir;
655
unsigned int max_len;
656
unsigned int offset;
657
);
658
};
659
"""
660
expected = """
661
struct page_pool_params {
662
struct page_pool_params_slow {
663
struct net_device *netdev;
664
unsigned int queue_idx;
665
unsigned int flags;
666
} slow;
667
struct page_pool_params_fast {
668
unsigned int order;
669
unsigned int pool_size;
670
int nid;
671
struct device *dev;
672
struct napi_struct *napi;
673
enum dma_data_direction dma_dir;
674
unsigned int max_len;
675
unsigned int offset;
676
} fast;
677
};
678
"""
679
680
result = self.apply_transforms("struct", line)
681
self.assertLogicallyEqual(result, expected)
682
683
def test_struct_kcov(self):
684
"""
685
"""
686
line = """
687
struct kcov {
688
refcount_t refcount;
689
spinlock_t lock;
690
enum kcov_mode mode __guarded_by(&lock);
691
unsigned int size __guarded_by(&lock);
692
void *area __guarded_by(&lock);
693
struct task_struct *t __guarded_by(&lock);
694
bool remote;
695
unsigned int remote_size;
696
int sequence;
697
};
698
"""
699
expected = """
700
"""
701
702
result = self.apply_transforms("struct", line)
703
self.assertLogicallyEqual(result, expected)
704
705
706
def test_struct_kcov(self):
707
"""
708
Test a struct from kernel/kcov.c.
709
"""
710
line = """
711
struct kcov {
712
refcount_t refcount;
713
spinlock_t lock;
714
enum kcov_mode mode __guarded_by(&lock);
715
unsigned int size __guarded_by(&lock);
716
void *area __guarded_by(&lock);
717
struct task_struct *t __guarded_by(&lock);
718
bool remote;
719
unsigned int remote_size;
720
int sequence;
721
};
722
"""
723
expected = """
724
struct kcov {
725
refcount_t refcount;
726
spinlock_t lock;
727
enum kcov_mode mode;
728
unsigned int size;
729
void *area;
730
struct task_struct *t;
731
bool remote;
732
unsigned int remote_size;
733
int sequence;
734
};
735
"""
736
737
result = self.apply_transforms("struct", line)
738
self.assertLogicallyEqual(result, expected)
739
740
def test_vars_stackdepot(self):
741
"""
742
Test guarded_by on vars from lib/stackdepot.c.
743
"""
744
line = """
745
size_t pool_offset __guarded_by(&pool_lock) = DEPOT_POOL_SIZE;
746
__guarded_by(&pool_lock) LIST_HEAD(free_stacks);
747
void **stack_pools __pt_guarded_by(&pool_lock);
748
"""
749
expected = """
750
size_t pool_offset = DEPOT_POOL_SIZE;
751
struct list_head free_stacks;
752
void **stack_pools;
753
"""
754
755
result = self.apply_transforms("var", line)
756
self.assertLogicallyEqual(result, expected)
757
758
def test_functions_with_acquires_and_releases(self):
759
"""
760
Test guarded_by on vars from lib/stackdepot.c.
761
"""
762
line = """
763
bool prepare_report_consumer(unsigned long *flags,
764
const struct access_info *ai,
765
struct other_info *other_info) \
766
__cond_acquires(true, &report_lock);
767
768
int tcp_sigpool_start(unsigned int id, struct tcp_sigpool *c) \
769
__cond_acquires(0, RCU_BH);
770
771
bool undo_report_consumer(unsigned long *flags,
772
const struct access_info *ai,
773
struct other_info *other_info) \
774
__cond_releases(true, &report_lock);
775
776
void debugfs_enter_cancellation(struct file *file,
777
struct debugfs_cancellation *c) \
778
__acquires(cancellation);
779
780
void debugfs_leave_cancellation(struct file *file,
781
struct debugfs_cancellation *c) \
782
__releases(cancellation);
783
784
acpi_cpu_flags acpi_os_acquire_lock(acpi_spinlock lockp) \
785
__acquires(lockp);
786
787
void acpi_os_release_lock(acpi_spinlock lockp,
788
acpi_cpu_flags not_used) \
789
__releases(lockp)
790
"""
791
expected = """
792
bool prepare_report_consumer(unsigned long *flags,
793
const struct access_info *ai,
794
struct other_info *other_info);
795
796
int tcp_sigpool_start(unsigned int id, struct tcp_sigpool *c);
797
798
bool undo_report_consumer(unsigned long *flags,
799
const struct access_info *ai,
800
struct other_info *other_info);
801
802
void debugfs_enter_cancellation(struct file *file,
803
struct debugfs_cancellation *c);
804
805
void debugfs_leave_cancellation(struct file *file,
806
struct debugfs_cancellation *c);
807
808
acpi_cpu_flags acpi_os_acquire_lock(acpi_spinlock lockp);
809
810
void acpi_os_release_lock(acpi_spinlock lockp,
811
acpi_cpu_flags not_used)
812
"""
813
814
result = self.apply_transforms("func", line)
815
self.assertLogicallyEqual(result, expected)
816
817
#
818
# Run all tests
819
#
820
if __name__ == "__main__":
821
run_unittest(__file__)
822
823