summaryrefslogtreecommitdiffstats
path: root/meta/recipes-kernel/lttng/lttng-tools_2.10.6.bb
blob: a3fabb20ec5570afc50f01769c815d613928ede6 (plain)
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
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
SECTION = "devel"
SUMMARY = "Linux Trace Toolkit Control"
DESCRIPTION = "The Linux trace toolkit is a suite of tools designed \
to extract program execution details from the Linux operating system \
and interpret them."

LICENSE = "GPLv2 & LGPLv2.1"
LIC_FILES_CHKSUM = "file://LICENSE;md5=01d7fc4496aacf37d90df90b90b0cac1 \
                    file://gpl-2.0.txt;md5=b234ee4d69f5fce4486a80fdaf4a4263 \
                    file://lgpl-2.1.txt;md5=0f0d71500e6a57fd24d825f33242b9ca"

DEPENDS = "liburcu popt libxml2 util-linux"
RDEPENDS_${PN} = "libgcc"
RDEPENDS_${PN}-ptest += "make perl bash gawk ${PN} babeltrace procps perl-module-overloading coreutils util-linux kmod"
RDEPENDS_${PN}-ptest_append_libc-glibc = " glibc-utils"
RDEPENDS_${PN}-ptest_append_libc-musl = " musl-utils"
# babelstats.pl wants getopt-long
RDEPENDS_${PN}-ptest += "perl-module-getopt-long"

PYTHON_OPTION = "am_cv_python_pyexecdir='${PYTHON_SITEPACKAGES_DIR}' \
                 am_cv_python_pythondir='${PYTHON_SITEPACKAGES_DIR}' \
                 PYTHON_INCLUDE='-I${STAGING_INCDIR}/python${PYTHON_BASEVERSION}${PYTHON_ABI}' \
"
PACKAGECONFIG ??= "lttng-ust"
PACKAGECONFIG[python] = "--enable-python-bindings ${PYTHON_OPTION},,python3 swig-native"
PACKAGECONFIG[lttng-ust] = "--with-lttng-ust, --without-lttng-ust, lttng-ust"
PACKAGECONFIG[kmod] = "--with-kmod, --without-kmod, kmod"
PACKAGECONFIG[manpages] = "--enable-man-pages, --disable-man-pages, asciidoc-native xmlto-native libxslt-native"
PACKAGECONFIG_remove_arc = "lttng-ust"

SRC_URI = "https://lttng.org/files/lttng-tools/lttng-tools-${PV}.tar.bz2 \
           file://x32.patch \
           file://run-ptest \
           file://lttng-sessiond.service \
           file://0001-Fix-tests-link-libpause_consumer-on-liblttng-ctl.patch \
           file://0002-Fix-test-skip-test_getcpu_override-on-single-thread-.patch \
           file://0003-Fix-test-unit-the-tree-origin-can-be-a-symlink-itsel.patch \
           file://0004-Skip-when-testapp-is-not-present.patch\
           file://0005-Tests-use-modprobe-to-test-for-the-presence-of-lttng.patch \
           file://0006-Tests-check-for-lttng-modules-presence.patch \
           file://0007-Fix-getgrnam-is-not-MT-Safe-use-getgrnam_r.patch \
           "

SRC_URI[md5sum] = "e88c521b5da6bb48a8187af633336ecc"
SRC_URI[sha256sum] = "f05df52bbebf8ce88d1b29e9e98cfc957d2ed738a345118018237ebdb581537c"

inherit autotools ptest pkgconfig useradd python3-dir manpages systemd

SYSTEMD_SERVICE_${PN} = "lttng-sessiond.service"
SYSTEMD_AUTO_ENABLE = "disable"

USERADD_PACKAGES = "${PN}"
GROUPADD_PARAM_${PN} = "tracing"

FILES_${PN} += "${libdir}/lttng/libexec/* ${datadir}/xml/lttng \
                ${PYTHON_SITEPACKAGES_DIR}/*"
FILES_${PN}-staticdev += "${PYTHON_SITEPACKAGES_DIR}/*.a"
FILES_${PN}-dev += "${PYTHON_SITEPACKAGES_DIR}/*.la"

# Since files are installed into ${libdir}/lttng/libexec we match 
# the libexec insane test so skip it.
# Python module needs to keep _lttng.so
INSANE_SKIP_${PN} = "libexec dev-so"
INSANE_SKIP_${PN}-dbg = "libexec"

do_install_append () {
    # install systemd unit file
    install -d ${D}${systemd_unitdir}/system
    install -m 0644 ${WORKDIR}/lttng-sessiond.service ${D}${systemd_unitdir}/system
}

do_install_ptest () {
    for f in Makefile tests/Makefile tests/utils/utils.sh tests/regression/tools/save-load/load-42*.lttng tests/regression/tools/save-load/configuration/load-42*.lttng ; do
        install -D "${B}/$f" "${D}${PTEST_PATH}/$f"
    done

    for f in config/tap-driver.sh config/test-driver src/common/config/session.xsd src/common/mi-lttng-3.0.xsd; do
        install -D "${S}/$f" "${D}${PTEST_PATH}/$f"
    done

    # Prevent 'make check' from recursing into non-test subdirectories.
    sed -i -e 's!^SUBDIRS = .*!SUBDIRS = tests!' "${D}${PTEST_PATH}/Makefile"

    # We don't need these
    sed -i -e '/dist_noinst_SCRIPTS = /,/^$/d' "${D}${PTEST_PATH}/tests/Makefile"

    # We shouldn't need to build anything in tests/utils
    sed -i -e 's!am__append_1 = . utils!am__append_1 = . !' \
        "${D}${PTEST_PATH}/tests/Makefile"

    # Copy the tests directory tree and the executables and
    # Makefiles found within.
    for d in $(find "${B}/tests" -type d -not -name .libs -printf '%P ') ; do
        install -d "${D}${PTEST_PATH}/tests/$d"
        find "${B}/tests/$d" -maxdepth 1 -executable -type f \
            -exec install -t "${D}${PTEST_PATH}/tests/$d" {} +
        # Take all .py scripts for tests using the python bindings.
        find "${B}/tests/$d" -maxdepth 1 -type f -name "*.py" \
            -exec install -t "${D}${PTEST_PATH}/tests/$d" {} +
        test -r "${B}/tests/$d/Makefile" && \
            install -t "${D}${PTEST_PATH}/tests/$d" "${B}/tests/$d/Makefile"
    done

    for d in $(find "${B}/tests" -type d -name .libs -printf '%P ') ; do
        for f in $(find "${B}/tests/$d" -maxdepth 1 -executable -type f -printf '%P ') ; do
            cp ${B}/tests/$d/$f ${D}${PTEST_PATH}/tests/`dirname $d`/$f
            case $f in
                *.so)
                    install -d ${D}${PTEST_PATH}/tests/$d/
                    ln -s  ../$f ${D}${PTEST_PATH}/tests/$d/$f
                    # Remove any rpath/runpath to pass QA check.
                    chrpath --delete ${D}${PTEST_PATH}/tests/$d/$f
                    ;;
            esac
        done
    done

    #
    # Use the versioned libs of liblttng-ust-dl.
    #
    ustdl="${D}${PTEST_PATH}/tests/regression/ust/ust-dl/test_ust-dl.py"
    if [ -e $ustdl ]; then
        sed -i -e 's!:liblttng-ust-dl.so!:liblttng-ust-dl.so.0!' $ustdl
    fi

    install ${B}/tests/unit/ini_config/sample.ini ${D}${PTEST_PATH}/tests/unit/ini_config/

    # We shouldn't need to build anything in tests/regression/tools
    sed -i -e 's!^SUBDIRS = tools !SUBDIRS = !' \
        "${D}${PTEST_PATH}/tests/regression/Makefile"

    # Prevent attempts to update Makefiles during test runs, and
    # silence "Making check in $SUBDIR" messages.
    find "${D}${PTEST_PATH}" -name Makefile -type f -exec \
        sed -i -e '/Makefile:/,/^$/d' -e '/%: %.in/,/^$/d' \
        -e '/echo "Making $$target in $$subdir"; \\/d' \
        -e 's/^srcdir = \(.*\)/srcdir = ./' \
        -e 's/^builddir = \(.*\)/builddir = ./' \
        -e 's/^all-am:.*/all-am:/' \
        {} +

    find "${D}${PTEST_PATH}" -name Makefile -type f -exec \
        touch -r "${B}/Makefile" {} +

    #
    # Need to stop generated binaries from rebuilding by removing their source dependencies
    #
    sed -e 's#\(^test.*OBJECTS.=\)#disable\1#g' \
        -e 's#\(^test.*DEPENDENCIES.=\)#disable\1#g' \
        -e 's#\(^test.*SOURCES.=\)#disable\1#g' \
        -e 's#\(^test.*LDADD.=\)#disable\1#g' \
        -i ${D}${PTEST_PATH}/tests/unit/Makefile

    # Substitute links to installed binaries.
    for prog in lttng lttng-relayd lttng-sessiond lttng-consumerd lttng-crash; do
        exedir="${D}${PTEST_PATH}/src/bin/${prog}"
        install -d "$exedir"
        case "$prog" in
            lttng-consumerd)
                ln -s "${libdir}/lttng/libexec/$prog" "$exedir"
                ;;
            *)
                ln -s "${bindir}/$prog" "$exedir"
                ;;
        esac
    done
}
ref='#n61'>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 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339
#!/usr/bin/env python
# ex:ts=4:sw=4:sts=4:et
# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-

# Copyright (c) 2013 Wind River Systems, Inc.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 as
# published by the Free Software Foundation.
#
# This program 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

from __future__ import print_function
import os
import sys
import getopt
import shutil
import re
import warnings
import subprocess
from optparse import OptionParser

scripts_path = os.path.abspath(os.path.dirname(os.path.abspath(sys.argv[0])))
lib_path = scripts_path + '/lib'
sys.path = sys.path + [lib_path]

import scriptpath

# Figure out where is the bitbake/lib/bb since we need bb.siggen and bb.process
bitbakepath = scriptpath.add_bitbake_lib_path()
if not bitbakepath:
    sys.stderr.write("Unable to find bitbake by searching parent directory of this script or PATH\n")
    sys.exit(1)

import bb.siggen
import bb.process

# Match the stamp's filename
# group(1): PE_PV (may no PE)
# group(2): PR
# group(3): TASK
# group(4): HASH
stamp_re = re.compile("(?P<pv>.*)-(?P<pr>r\d+)\.(?P<task>do_\w+)\.(?P<hash>[^\.]*)")
sigdata_re = re.compile(".*\.sigdata\..*")

def gen_dict(stamps):
    """
    Generate the dict from the stamps dir.
    The output dict format is:
    {fake_f: {pn: PN, pv: PV, pr: PR, task: TASK, path: PATH}}
    Where:
    fake_f: pv + task + hash
    path: the path to the stamp file
    """
    # The member of the sub dict (A "path" will be appended below)
    sub_mem = ("pv", "pr", "task")
    d = {}
    for dirpath, _, files in os.walk(stamps):
        for f in files:
            # The "bitbake -S" would generate ".sigdata", but no "_setscene".
            fake_f = re.sub('_setscene.', '.', f)
            fake_f = re.sub('.sigdata', '', fake_f)
            subdict = {}
            tmp = stamp_re.match(fake_f)
            if tmp:
                for i in sub_mem:
                    subdict[i] = tmp.group(i)
                if len(subdict) != 0:
                    pn = os.path.basename(dirpath)
                    subdict['pn'] = pn
                    # The path will be used by os.stat() and bb.siggen
                    subdict['path'] = dirpath + "/" + f
                    fake_f = tmp.group('pv') + tmp.group('task') + tmp.group('hash')
                    d[fake_f] = subdict
    return d

# Re-construct the dict
def recon_dict(dict_in):
    """
    The output dict format is:
    {pn_task: {pv: PV, pr: PR, path: PATH}}
    """
    dict_out = {}
    for k in dict_in.keys():
        subdict = {}
        # The key
        pn_task = "%s_%s" % (dict_in.get(k).get('pn'), dict_in.get(k).get('task'))
        # If more than one stamps are found, use the latest one.
        if pn_task in dict_out:
            full_path_pre = dict_out.get(pn_task).get('path')
            full_path_cur = dict_in.get(k).get('path')
            if os.stat(full_path_pre).st_mtime > os.stat(full_path_cur).st_mtime:
                continue
        subdict['pv'] = dict_in.get(k).get('pv')
        subdict['pr'] = dict_in.get(k).get('pr')
        subdict['path'] = dict_in.get(k).get('path')
        dict_out[pn_task] = subdict

    return dict_out

def split_pntask(s):
    """
    Split the pn_task in to (pn, task) and return it
    """
    tmp = re.match("(.*)_(do_.*)", s)
    return (tmp.group(1), tmp.group(2))


def print_added(d_new = None, d_old = None):
    """
    Print the newly added tasks
    """
    added = {}
    for k in d_new.keys():
        if k not in d_old:
            # Add the new one to added dict, and remove it from
            # d_new, so the remaining ones are the changed ones
            added[k] = d_new.get(k)
            del(d_new[k])

    if not added:
        return 0

    # Format the output, the dict format is:
    # {pn: task1, task2 ...}
    added_format = {}
    counter = 0
    for k in added.keys():
        pn, task = split_pntask(k)
        if pn in added_format:
            # Append the value
            added_format[pn] = "%s %s" % (added_format.get(pn), task)
        else:
            added_format[pn] = task
        counter += 1
    print("=== Newly added tasks: (%s tasks)" % counter)
    for k in added_format.keys():
        print("  %s: %s" % (k, added_format.get(k)))

    return counter

def print_vrchanged(d_new = None, d_old = None, vr = None):
    """
    Print the pv or pr changed tasks.
    The arg "vr" is "pv" or "pr"
    """
    pvchanged = {}
    counter = 0
    for k in d_new.keys():
        if d_new.get(k).get(vr) != d_old.get(k).get(vr):
            counter += 1
            pn, task = split_pntask(k)
            if pn not in pvchanged:
                # Format the output, we only print pn (no task) since
                # all the tasks would be changed when pn or pr changed,
                # the dict format is:
                # {pn: pv/pr_old -> pv/pr_new}
                pvchanged[pn] = "%s -> %s" % (d_old.get(k).get(vr), d_new.get(k).get(vr))
            del(d_new[k])

    if not pvchanged:
        return 0

    print("\n=== %s changed: (%s tasks)" % (vr.upper(), counter))
    for k in pvchanged.keys():
        print("  %s: %s" % (k, pvchanged.get(k)))

    return counter

def print_depchanged(d_new = None, d_old = None, verbose = False):
    """
    Print the dependency changes
    """
    depchanged = {}
    counter = 0
    for k in d_new.keys():
        counter += 1
        pn, task = split_pntask(k)
        if (verbose):
            full_path_old = d_old.get(k).get("path")
            full_path_new = d_new.get(k).get("path")
            # No counter since it is not ready here
            if sigdata_re.match(full_path_old) and sigdata_re.match(full_path_new):
                output = bb.siggen.compare_sigfiles(full_path_old, full_path_new)
                if output:
                    print("\n=== The verbose changes of %s.do_%s:" % (pn, task))
                    print('\n'.join(output))
        else:
            # Format the output, the format is:
            # {pn: task1, task2, ...}
            if pn in depchanged:
                depchanged[pn] = "%s %s" % (depchanged.get(pn), task)
            else:
                depchanged[pn] = task

    if len(depchanged) > 0:
        print("\n=== Dependencies changed: (%s tasks)" % counter)
        for k in depchanged.keys():
            print("  %s: %s" % (k, depchanged[k]))

    return counter


def main():
    """
    Print what will be done between the current and last builds:
    1) Run "STAMPS_DIR=<path> bitbake -S recipe" to re-generate the stamps
    2) Figure out what are newly added and changed, can't figure out
       what are removed since we can't know the previous stamps
       clearly, for example, if there are several builds, we can't know
       which stamps the last build has used exactly.
    3) Use bb.siggen.compare_sigfiles to diff the old and new stamps
    """

    parser = OptionParser(
        version = "1.0",
        usage = """%prog [options] [package ...]
print what will be done between the current and last builds, for example:

    $ bitbake core-image-sato
    # Edit the recipes
    $ bitbake-whatchanged core-image-sato

The changes will be printed"

Note:
    The amount of tasks is not accurate when the task is "do_build" since
    it usually depends on other tasks.
    The "nostamp" task is not included.
"""
)
    parser.add_option("-v", "--verbose", help = "print the verbose changes",
               action = "store_true", dest = "verbose")

    options, args = parser.parse_args(sys.argv)

    verbose = options.verbose

    if len(args) != 2:
        parser.error("Incorrect number of arguments")
    else:
        recipe = args[1]

    # Get the STAMPS_DIR
    print("Figuring out the STAMPS_DIR ...")
    cmdline = "bitbake -e | sed -ne 's/^STAMPS_DIR=\"\(.*\)\"/\\1/p'"
    try:
        stampsdir, err = bb.process.run(cmdline)
    except:
        raise
    if not stampsdir:
        print("ERROR: No STAMPS_DIR found for '%s'" % recipe, file=sys.stderr)
        return 2
    stampsdir = stampsdir.rstrip("\n")
    if not os.path.isdir(stampsdir):
        print("ERROR: stamps directory \"%s\" not found!" % stampsdir, file=sys.stderr)
        return 2

    # The new stamps dir
    new_stampsdir = stampsdir + ".bbs"
    if os.path.exists(new_stampsdir):
        print("ERROR: %s already exists!" % new_stampsdir, file=sys.stderr)
        return 2

    try:
        # Generate the new stamps dir
        print("Generating the new stamps ... (need several minutes)")
        cmdline = "STAMPS_DIR=%s bitbake -S none %s" % (new_stampsdir, recipe)
        # FIXME
        # The "bitbake -S" may fail, not fatal error, the stamps will still
        # be generated, this might be a bug of "bitbake -S".
        try:
            bb.process.run(cmdline)
        except Exception as exc:
            print(exc)

        # The dict for the new and old stamps.
        old_dict = gen_dict(stampsdir)
        new_dict = gen_dict(new_stampsdir)

        # Remove the same one from both stamps.
        cnt_unchanged = 0
        for k in new_dict.keys():
            if k in old_dict:
                cnt_unchanged += 1
                del(new_dict[k])
                del(old_dict[k])

        # Re-construct the dict to easily find out what is added or changed.
        # The dict format is:
        # {pn_task: {pv: PV, pr: PR, path: PATH}}
        new_recon = recon_dict(new_dict)
        old_recon = recon_dict(old_dict)

        del new_dict
        del old_dict

        # Figure out what are changed, the new_recon would be changed
        # by the print_xxx function.
        # Newly added
        cnt_added = print_added(new_recon, old_recon)

        # PV (including PE) and PR changed
        # Let the bb.siggen handle them if verbose
        cnt_rv = {}
        if not verbose:
            for i in ('pv', 'pr'):
               cnt_rv[i] = print_vrchanged(new_recon, old_recon, i)

        # Dependencies changed (use bitbake-diffsigs)
        cnt_dep = print_depchanged(new_recon, old_recon, verbose)

        total_changed = cnt_added + (cnt_rv.get('pv') or 0) + (cnt_rv.get('pr') or 0) + cnt_dep

        print("\n=== Summary: (%s changed, %s unchanged)" % (total_changed, cnt_unchanged))
        if verbose:
            print("Newly added: %s\nDependencies changed: %s\n" % \
                (cnt_added, cnt_dep))
        else:
            print("Newly added: %s\nPV changed: %s\nPR changed: %s\nDependencies changed: %s\n" % \
                (cnt_added, cnt_rv.get('pv') or 0, cnt_rv.get('pr') or 0, cnt_dep))
    except:
        print("ERROR occurred!")
        raise
    finally:
        # Remove the newly generated stamps dir
        if os.path.exists(new_stampsdir):
            print("Removing the newly generated stamps dir ...")
            shutil.rmtree(new_stampsdir)

if __name__ == "__main__":
    sys.exit(main())