about summary refs log tree commit diff
path: root/scripts/tst-ld-trace.py
blob: 248d1c20ef057e8d6e84da57fb9b3d668234d450 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
#!/usr/bin/python3
# Dump the output of LD_TRACE_LOADED_OBJECTS in architecture neutral format.
# Copyright (C) 2022-2023 Free Software Foundation, Inc.
# Copyright The GNU Toolchain Authors.
# This file is part of the GNU C Library.
#
# The GNU C Library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# The GNU C Library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with the GNU C Library; if not, see
# <https://www.gnu.org/licenses/>.

import argparse
import os
import subprocess
import sys

try:
    subprocess.run
except:
    class _CompletedProcess:
        def __init__(self, args, returncode, stdout=None, stderr=None):
            self.args = args
            self.returncode = returncode
            self.stdout = stdout
            self.stderr = stderr

    def _run(*popenargs, input=None, timeout=None, check=False, **kwargs):
        assert(timeout is None)
        with subprocess.Popen(*popenargs, **kwargs) as process:
            try:
                stdout, stderr = process.communicate(input)
            except:
                process.kill()
                process.wait()
                raise
            returncode = process.poll()
            if check and returncode:
                raise subprocess.CalledProcessError(returncode, popenargs)
        return _CompletedProcess(popenargs, returncode, stdout, stderr)

    subprocess.run = _run

def is_vdso(lib):
    return lib.startswith('linux-gate') or lib.startswith('linux-vdso')


def parse_trace(cmd, fref):
    new_env = os.environ.copy()
    new_env['LD_TRACE_LOADED_OBJECTS'] = '1'
    trace_out = subprocess.run(cmd, stdout=subprocess.PIPE, check=True,
                               universal_newlines=True, env=new_env).stdout
    trace = []
    for line in trace_out.splitlines():
        line = line.strip()
        if is_vdso(line):
            continue
        fields = line.split('=>' if '=>' in line else ' ')
        lib = os.path.basename(fields[0].strip())
        if lib.startswith('ld'):
            lib = 'ld'
        elif lib.startswith('libc'):
            lib = 'libc'
        found = 1 if fields[1].strip() != 'not found' else 0
        trace += ['{} {}'.format(lib, found)]
    trace = sorted(trace)

    reference = sorted(line.replace('\n','') for line in fref.readlines())

    ret = 0 if trace == reference else 1
    if ret != 0:
        for i in reference:
            if i not in trace:
                print("Only in {}: {}".format(fref.name, i))
        for i in trace:
            if i not in reference:
                print("Only in trace: {}".format(i))

    sys.exit(ret)


def get_parser():
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument('command',
                        help='comand to run')
    parser.add_argument('reference',
                        help='reference file to compare')
    return parser


def main(argv):
    parser = get_parser()
    opts = parser.parse_args(argv)
    with open(opts.reference, 'r') as fref:
        # Remove the initial 'env' command.
        parse_trace(opts.command.split()[1:], fref)


if __name__ == '__main__':
    main(sys.argv[1:])