diff --git a/.bcc.metadata b/.bcc.metadata new file mode 100644 index 0000000..933df45 --- /dev/null +++ b/.bcc.metadata @@ -0,0 +1 @@ +d5a11c0da3483cad478f6971cbfc9b0eb1bdc932 SOURCES/bcc-0.7.0.tar.gz diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6f4147b --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +SOURCES/bcc-0.7.0.tar.gz diff --git a/SOURCES/Bytes-string-encoding-2004.patch b/SOURCES/Bytes-string-encoding-2004.patch new file mode 100644 index 0000000..5597057 --- /dev/null +++ b/SOURCES/Bytes-string-encoding-2004.patch @@ -0,0 +1,791 @@ +From b96ebcd2f328cd694101d657341faeb5869556a9 Mon Sep 17 00:00:00 2001 +From: jeromemarchand <38073585+jeromemarchand@users.noreply.github.com> +Date: Wed, 10 Oct 2018 01:58:15 +0200 +Subject: [PATCH] Bytes/string encoding (#2004) + +* tools: uses 'replace' error handler by default in decode() + +Tools might encouter characters from non utf-8 charset (e.g. a file +name). When this happen, it's better to replace the unexpected +character by a question mark than crash the tool when all we do is +to print the string. + +* tools: fix a bytes/string issue in attach_perf_event() +--- + tools/bashreadline.py | 2 +- + tools/biosnoop.py | 4 ++-- + tools/biotop.py | 4 ++-- + tools/btrfsslower.py | 10 ++++++---- + tools/cachetop.py | 2 +- + tools/capable.py | 4 ++-- + tools/dcsnoop.py | 5 +++-- + tools/ext4slower.py | 10 ++++++---- + tools/filelife.py | 3 ++- + tools/fileslower.py | 6 +++--- + tools/filetop.py | 7 ++++--- + tools/funcslower.py | 4 ++-- + tools/gethostlatency.py | 4 ++-- + tools/hardirqs.py | 2 +- + tools/llcstat.py | 2 +- + tools/mdflush.py | 3 ++- + tools/mountsnoop.py | 4 ++-- + tools/nfsslower.py | 4 ++-- + tools/offcputime.py | 4 ++-- + tools/offwaketime.py | 8 ++++---- + tools/old/offcputime.py | 2 +- + tools/old/oomkill.py | 4 ++-- + tools/old/profile.py | 2 +- + tools/old/wakeuptime.py | 8 ++++---- + tools/oomkill.py | 4 ++-- + tools/opensnoop.py | 3 ++- + tools/profile.py | 8 ++++---- + tools/sslsniff.py | 5 +++-- + tools/stackcount.py | 2 +- + tools/statsnoop.py | 5 +++-- + tools/tcpaccept.py | 7 ++++--- + tools/tcpconnect.py | 7 ++++--- + tools/tcpconnlat.py | 8 ++++---- + tools/tcplife.py | 4 ++-- + tools/tcpstates.py | 4 ++-- + tools/tcptracer.py | 4 ++-- + tools/trace.py | 3 ++- + tools/ttysnoop.py | 2 +- + tools/zfsslower.py | 10 ++++++---- + 39 files changed, 100 insertions(+), 84 deletions(-) + +diff --git a/tools/bashreadline.py b/tools/bashreadline.py +index aaf6fc7..89c37c3 100755 +--- a/tools/bashreadline.py ++++ b/tools/bashreadline.py +@@ -57,7 +57,7 @@ print("%-9s %-6s %s" % ("TIME", "PID", "COMMAND")) + def print_event(cpu, data, size): + event = ct.cast(data, ct.POINTER(Data)).contents + print("%-9s %-6d %s" % (strftime("%H:%M:%S"), event.pid, +- event.str.decode())) ++ event.str.decode('utf-8', 'replace'))) + + b["events"].open_perf_buffer(print_event) + while 1: +diff --git a/tools/biosnoop.py b/tools/biosnoop.py +index 7f61180..2b1e77d 100755 +--- a/tools/biosnoop.py ++++ b/tools/biosnoop.py +@@ -176,8 +176,8 @@ delta = 0 + delta = float(delta) + (event.ts - prev_ts) + + print("%-14.9f %-14.14s %-6s %-7s %-2s %-9s %-7s %7.2f" % ( +- delta / 1000000, event.name.decode(), event.pid, +- event.disk_name.decode(), rwflg, val, ++ delta / 1000000, event.name.decode('utf-8', 'replace'), event.pid, ++ event.disk_name.decode('utf-8', 'replace'), rwflg, val, + event.len, float(event.delta) / 1000000)) + + prev_ts = event.ts +diff --git a/tools/biotop.py b/tools/biotop.py +index 63d6642..c6e1ca2 100755 +--- a/tools/biotop.py ++++ b/tools/biotop.py +@@ -221,8 +221,8 @@ exiting = 0 + # print line + avg_ms = (float(v.us) / 1000) / v.io + print("%-6d %-16s %1s %-3d %-3d %-8s %5s %7s %6.2f" % (k.pid, +- k.name.decode(), "W" if k.rwflag else "R", k.major, k.minor, +- diskname, v.io, v.bytes / 1024, avg_ms)) ++ k.name.decode('utf-8', 'replace'), "W" if k.rwflag else "R", ++ k.major, k.minor, diskname, v.io, v.bytes / 1024, avg_ms)) + + line += 1 + if line >= maxrows: +diff --git a/tools/btrfsslower.py b/tools/btrfsslower.py +index 644cb22..d48e04c 100755 +--- a/tools/btrfsslower.py ++++ b/tools/btrfsslower.py +@@ -316,12 +316,14 @@ TASK_COMM_LEN = 16 # linux/sched.h + + if (csv): + print("%d,%s,%d,%s,%d,%d,%d,%s" % ( +- event.ts_us, event.task.decode(), event.pid, type, event.size, +- event.offset, event.delta_us, event.file.decode())) ++ event.ts_us, event.task.decode('utf-8', 'replace'), event.pid, ++ type, event.size, event.offset, event.delta_us, ++ event.file.decode('utf-8', 'replace'))) + return + print("%-8s %-14.14s %-6s %1s %-7s %-8d %7.2f %s" % (strftime("%H:%M:%S"), +- event.task.decode(), event.pid, type, event.size, event.offset / 1024, +- float(event.delta_us) / 1000, event.file.decode())) ++ event.task.decode('utf-8', 'replace'), event.pid, type, event.size, ++ event.offset / 1024, float(event.delta_us) / 1000, ++ event.file.decode('utf-8', 'replace'))) + + # initialize BPF + b = BPF(text=bpf_text) +diff --git a/tools/cachetop.py b/tools/cachetop.py +index 0e08af9..1013675 100755 +--- a/tools/cachetop.py ++++ b/tools/cachetop.py +@@ -72,7 +72,7 @@ def get_processes_stats( + counts = bpf.get_table("counts") + stats = defaultdict(lambda: defaultdict(int)) + for k, v in counts.items(): +- stats["%d-%d-%s" % (k.pid, k.uid, k.comm.decode())][k.ip] = v.value ++ stats["%d-%d-%s" % (k.pid, k.uid, k.comm.decode('utf-8', 'replace'))][k.ip] = v.value + stats_list = [] + + for pid, count in sorted(stats.items(), key=lambda stat: stat[0]): +diff --git a/tools/capable.py b/tools/capable.py +index 3e032e9..efcff4d 100755 +--- a/tools/capable.py ++++ b/tools/capable.py +@@ -148,8 +148,8 @@ print("%-9s %-6s %-6s %-16s %-4s %-20s %s" % ( + else: + name = "?" + print("%-9s %-6d %-6d %-16s %-4d %-20s %d" % (strftime("%H:%M:%S"), +- event.uid, event.pid, event.comm.decode(), event.cap, name, +- event.audit)) ++ event.uid, event.pid, event.comm.decode('utf-8', 'replace'), ++ event.cap, name, event.audit)) + + # loop with callback to print_event + b["events"].open_perf_buffer(print_event) +diff --git a/tools/dcsnoop.py b/tools/dcsnoop.py +index 070c87a..13152c2 100755 +--- a/tools/dcsnoop.py ++++ b/tools/dcsnoop.py +@@ -153,8 +153,9 @@ start_ts = time.time() + def print_event(cpu, data, size): + event = ct.cast(data, ct.POINTER(Data)).contents + print("%-11.6f %-6d %-16s %1s %s" % ( +- time.time() - start_ts, event.pid, event.comm.decode(), +- mode_s[event.type], event.filename.decode())) ++ time.time() - start_ts, event.pid, ++ event.comm.decode('utf-8', 'replace'), mode_s[event.type], ++ event.filename.decode('utf-8', 'replace'))) + + # header + print("%-11s %-6s %-16s %1s %s" % ("TIME(s)", "PID", "COMM", "T", "FILE")) +diff --git a/tools/ext4slower.py b/tools/ext4slower.py +index eb6430e..88db831 100755 +--- a/tools/ext4slower.py ++++ b/tools/ext4slower.py +@@ -314,12 +314,14 @@ TASK_COMM_LEN = 16 # linux/sched.h + + if (csv): + print("%d,%s,%d,%s,%d,%d,%d,%s" % ( +- event.ts_us, event.task.decode(), event.pid, type, event.size, +- event.offset, event.delta_us, event.file.decode())) ++ event.ts_us, event.task.decode('utf-8', 'replace'), event.pid, ++ type, event.size, event.offset, event.delta_us, ++ event.file.decode('utf-8', 'replace'))) + return + print("%-8s %-14.14s %-6s %1s %-7s %-8d %7.2f %s" % (strftime("%H:%M:%S"), +- event.task.decode(), event.pid, type, event.size, event.offset / 1024, +- float(event.delta_us) / 1000, event.file.decode())) ++ event.task.decode('utf-8', 'replace'), event.pid, type, event.size, ++ event.offset / 1024, float(event.delta_us) / 1000, ++ event.file.decode('utf-8', 'replace'))) + + # initialize BPF + b = BPF(text=bpf_text) +diff --git a/tools/filelife.py b/tools/filelife.py +index 0f4e269..410659d 100755 +--- a/tools/filelife.py ++++ b/tools/filelife.py +@@ -136,7 +136,8 @@ print("%-8s %-6s %-16s %-7s %s" % ("TIME", "PID", "COMM", "AGE(s)", "FILE")) + def print_event(cpu, data, size): + event = ct.cast(data, ct.POINTER(Data)).contents + print("%-8s %-6d %-16s %-7.2f %s" % (strftime("%H:%M:%S"), event.pid, +- event.comm.decode(), float(event.delta) / 1000, event.fname.decode())) ++ event.comm.decode('utf-8', 'replace'), float(event.delta) / 1000, ++ event.fname.decode('utf-8', 'replace'))) + + b["events"].open_perf_buffer(print_event) + while 1: +diff --git a/tools/fileslower.py b/tools/fileslower.py +index 5caa4ca..25443a2 100755 +--- a/tools/fileslower.py ++++ b/tools/fileslower.py +@@ -240,13 +240,13 @@ start_ts = time.time() + event = ct.cast(data, ct.POINTER(Data)).contents + + ms = float(event.delta_us) / 1000 +- name = event.name.decode() ++ name = event.name.decode('utf-8', 'replace') + if event.name_len > DNAME_INLINE_LEN: + name = name[:-3] + "..." + + print("%-8.3f %-14.14s %-6s %1s %-7s %7.2f %s" % ( +- time.time() - start_ts, event.comm.decode(), event.pid, +- mode_s[event.mode], event.sz, ms, name)) ++ time.time() - start_ts, event.comm.decode('utf-8', 'replace'), ++ event.pid, mode_s[event.mode], event.sz, ms, name)) + + b["events"].open_perf_buffer(print_event, page_cnt=64) + while 1: +diff --git a/tools/filetop.py b/tools/filetop.py +index 454dfd8..4c7a28a 100755 +--- a/tools/filetop.py ++++ b/tools/filetop.py +@@ -190,14 +190,15 @@ exiting = 0 + for k, v in reversed(sorted(counts.items(), + key=lambda counts: + getattr(counts[1], args.sort))): +- name = k.name.decode() ++ name = k.name.decode('utf-8', 'replace') + if k.name_len > DNAME_INLINE_LEN: + name = name[:-3] + "..." + + # print line + print("%-6d %-16s %-6d %-6d %-7d %-7d %1s %s" % (k.pid, +- k.comm.decode(), v.reads, v.writes, v.rbytes / 1024, +- v.wbytes / 1024, k.type.decode(), name)) ++ k.comm.decode('utf-8', 'replace'), v.reads, v.writes, ++ v.rbytes / 1024, v.wbytes / 1024, ++ k.type.decode('utf-8', 'replace'), name)) + + line += 1 + if line >= maxrows: +diff --git a/tools/funcslower.py b/tools/funcslower.py +index 93fb846..261869e 100755 +--- a/tools/funcslower.py ++++ b/tools/funcslower.py +@@ -306,7 +306,7 @@ earliest_ts = 0 + # print folded stack output + user_stack = list(user_stack) + kernel_stack = list(kernel_stack) +- line = [event.comm.decode()] + \ ++ line = [event.comm.decode('utf-8', 'replace')] + \ + [b.sym(addr, event.tgid_pid) for addr in reversed(user_stack)] + \ + (do_delimiter and ["-"] or []) + \ + [b.ksym(addr) for addr in reversed(kernel_stack)] +@@ -323,7 +323,7 @@ earliest_ts = 0 + ts = float(event.duration_ns) / time_multiplier + if not args.folded: + print((time_str(event) + "%-14.14s %-6s %7.2f %16x %s %s") % +- (event.comm.decode(), event.tgid_pid >> 32, ++ (event.comm.decode('utf-8', 'replace'), event.tgid_pid >> 32, + ts, event.retval, args.functions[event.id], args_str(event))) + if args.user_stack or args.kernel_stack: + print_stack(event) +diff --git a/tools/gethostlatency.py b/tools/gethostlatency.py +index f1d7dea..3a967ae 100755 +--- a/tools/gethostlatency.py ++++ b/tools/gethostlatency.py +@@ -129,8 +129,8 @@ print("%-9s %-6s %-16s %10s %s" % ("TIME", "PID", "COMM", "LATms", "HOST")) + def print_event(cpu, data, size): + event = ct.cast(data, ct.POINTER(Data)).contents + print("%-9s %-6d %-16s %10.2f %s" % (strftime("%H:%M:%S"), event.pid, +- event.comm.decode(), (float(event.delta) / 1000000), +- event.host.decode())) ++ event.comm.decode('utf-8', 'replace'), (float(event.delta) / 1000000), ++ event.host.decode('utf-8', 'replace'))) + + # loop with callback to print_event + b["events"].open_perf_buffer(print_event) +diff --git a/tools/hardirqs.py b/tools/hardirqs.py +index 3835d63..589a890 100755 +--- a/tools/hardirqs.py ++++ b/tools/hardirqs.py +@@ -172,7 +172,7 @@ dist = b.get_table("dist") + else: + print("%-26s %11s" % ("HARDIRQ", "TOTAL_" + label)) + for k, v in sorted(dist.items(), key=lambda dist: dist[1].value): +- print("%-26s %11d" % (k.name.decode(), v.value / factor)) ++ print("%-26s %11d" % (k.name.decode('utf-8', 'replace'), v.value / factor)) + dist.clear() + + countdown -= 1 +diff --git a/tools/llcstat.py b/tools/llcstat.py +index fe8bdd9..ec2c1f8 100755 +--- a/tools/llcstat.py ++++ b/tools/llcstat.py +@@ -113,7 +113,7 @@ tot_miss = 0 + # This happens on some PIDs due to missed counts caused by sampling + hit = (v.value - miss) if (v.value >= miss) else 0 + print('{:<8d} {:<16s} {:<4d} {:>12d} {:>12d} {:>6.2f}%'.format( +- k.pid, k.name.decode(), k.cpu, v.value, miss, ++ k.pid, k.name.decode('utf-8', 'replace'), k.cpu, v.value, miss, + (float(hit) / float(v.value)) * 100.0)) + print('Total References: {} Total Misses: {} Hit Rate: {:.2f}%'.format( + tot_ref, tot_miss, (float(tot_ref - tot_miss) / float(tot_ref)) * 100.0)) +diff --git a/tools/mdflush.py b/tools/mdflush.py +index 1d29bf1..70afc4d 100755 +--- a/tools/mdflush.py ++++ b/tools/mdflush.py +@@ -72,7 +72,8 @@ print("%-8s %-6s %-16s %s" % ("TIME", "PID", "COMM", "DEVICE")) + def print_event(cpu, data, size): + event = ct.cast(data, ct.POINTER(Data)).contents + print("%-8s %-6d %-16s %s" % (strftime("%H:%M:%S"), event.pid, +- event.comm.decode(), event.disk.decode())) ++ event.comm.decode('utf-8', 'replace'), ++ event.disk.decode('utf-8', 'replace'))) + + # read events + b["events"].open_perf_buffer(print_event) +diff --git a/tools/mountsnoop.py b/tools/mountsnoop.py +index 2d0fa1a..e9b5865 100755 +--- a/tools/mountsnoop.py ++++ b/tools/mountsnoop.py +@@ -382,8 +382,8 @@ _escape_chars = { + flags=decode_umount_flags(syscall['flags']), + retval=decode_errno(event.union.retval)) + print('{:16} {:<7} {:<7} {:<11} {}'.format( +- syscall['comm'].decode(), syscall['tgid'], syscall['pid'], +- syscall['mnt_ns'], call)) ++ syscall['comm'].decode('utf-8', 'replace'), syscall['tgid'], ++ syscall['pid'], syscall['mnt_ns'], call)) + except KeyError: + # This might happen if we lost an event. + pass +diff --git a/tools/nfsslower.py b/tools/nfsslower.py +index 0f836af..2f92c90 100755 +--- a/tools/nfsslower.py ++++ b/tools/nfsslower.py +@@ -280,13 +280,13 @@ TASK_COMM_LEN = 16 # linux/sched.h + return + print("%-8s %-14.14s %-6s %1s %-7s %-8d %7.2f %s" % + (strftime("%H:%M:%S"), +- event.task.decode(), ++ event.task.decode('utf-8', 'replace'), + event.pid, + type, + event.size, + event.offset / 1024, + float(event.delta_us) / 1000, +- event.file.decode())) ++ event.file.decode('utf-8', 'replace'))) + + + # Currently specifically works for NFSv4, the other kprobes are generic +diff --git a/tools/offcputime.py b/tools/offcputime.py +index de77fb4..d84ae52 100755 +--- a/tools/offcputime.py ++++ b/tools/offcputime.py +@@ -281,7 +281,7 @@ stack_traces = b.get_table("stack_traces") + # print folded stack output + user_stack = list(user_stack) + kernel_stack = list(kernel_stack) +- line = [k.name.decode()] ++ line = [k.name.decode('utf-8', 'replace')] + # if we failed to get the stack is, such as due to no space (-ENOMEM) or + # hash collision (-EEXIST), we still print a placeholder for consistency + if not args.kernel_stacks_only: +@@ -312,7 +312,7 @@ stack_traces = b.get_table("stack_traces") + else: + for addr in user_stack: + print(" %s" % b.sym(addr, k.tgid)) +- print(" %-16s %s (%d)" % ("-", k.name.decode(), k.pid)) ++ print(" %-16s %s (%d)" % ("-", k.name.decode('utf-8', 'replace'), k.pid)) + print(" %d\n" % v.value) + + if missing_stacks > 0: +diff --git a/tools/offwaketime.py b/tools/offwaketime.py +index 01961ee..674be22 100755 +--- a/tools/offwaketime.py ++++ b/tools/offwaketime.py +@@ -316,7 +316,7 @@ need_delimiter = args.delimited and not (args.kernel_stacks_only or + + if folded: + # print folded stack output +- line = [k.target.decode()] ++ line = [k.target.decode('utf-8', 'replace')] + if not args.kernel_stacks_only: + if stack_id_err(k.t_u_stack_id): + line.append("[Missed User Stack]") +@@ -344,11 +344,11 @@ need_delimiter = args.delimited and not (args.kernel_stacks_only or + else: + line.extend([b.sym(addr, k.w_tgid) + for addr in reversed(list(waker_user_stack))]) +- line.append(k.waker.decode()) ++ line.append(k.waker.decode('utf-8', 'replace')) + print("%s %d" % (";".join(line), v.value)) + else: + # print wakeup name then stack in reverse order +- print(" %-16s %s %s" % ("waker:", k.waker.decode(), k.t_pid)) ++ print(" %-16s %s %s" % ("waker:", k.waker.decode('utf-8', 'replace'), k.t_pid)) + if not args.kernel_stacks_only: + if stack_id_err(k.w_u_stack_id): + print(" [Missed User Stack]") +@@ -381,7 +381,7 @@ need_delimiter = args.delimited and not (args.kernel_stacks_only or + else: + for addr in target_user_stack: + print(" %s" % b.sym(addr, k.t_tgid)) +- print(" %-16s %s %s" % ("target:", k.target.decode(), k.w_pid)) ++ print(" %-16s %s %s" % ("target:", k.target.decode('utf-8', 'replace'), k.w_pid)) + print(" %d\n" % v.value) + + if missing_stacks > 0: +diff --git a/tools/old/offcputime.py b/tools/old/offcputime.py +index 680d924..38d12a2 100755 +--- a/tools/old/offcputime.py ++++ b/tools/old/offcputime.py +@@ -185,7 +185,7 @@ matched = b.num_open_kprobes() + for k, v in sorted(counts.items(), key=lambda counts: counts[1].value): + if folded: + # print folded stack output +- line = k.name.decode() + ";" ++ line = k.name.decode('utf-8', 'replace') + ";" + for i in reversed(range(0, maxdepth)): + if k.ret[i] == 0: + continue +diff --git a/tools/old/oomkill.py b/tools/old/oomkill.py +index e8e0321..b99f852 100755 +--- a/tools/old/oomkill.py ++++ b/tools/old/oomkill.py +@@ -68,8 +68,8 @@ TASK_COMM_LEN = 16 # linux/sched.h + avgline = stats.read().rstrip() + print(("%s Triggered by PID %d (\"%s\"), OOM kill of PID %d (\"%s\")" + ", %d pages, loadavg: %s") % (strftime("%H:%M:%S"), event.fpid, +- event.fcomm.decode(), event.tpid, event.tcomm.decode(), event.pages, +- avgline)) ++ event.fcomm.decode('utf-8', 'replace'), event.tpid, ++ event.tcomm.decode('utf-8', 'replace'), event.pages, avgline)) + + # initialize BPF + b = BPF(text=bpf_text) +diff --git a/tools/old/profile.py b/tools/old/profile.py +index 04ca13a..e308208 100755 +--- a/tools/old/profile.py ++++ b/tools/old/profile.py +@@ -339,7 +339,7 @@ stack_traces = b.get_table("stack_traces") + # print folded stack output + user_stack = list(user_stack) + kernel_stack = list(kernel_stack) +- line = [k.name.decode()] + \ ++ line = [k.name.decode('utf-8', 'replace')] + \ + [b.sym(addr, k.pid) for addr in reversed(user_stack)] + \ + (do_delimiter and ["-"] or []) + \ + [aksym(addr) for addr in reversed(kernel_stack)] +diff --git a/tools/old/wakeuptime.py b/tools/old/wakeuptime.py +index e09840a..783c7ff 100644 +--- a/tools/old/wakeuptime.py ++++ b/tools/old/wakeuptime.py +@@ -199,23 +199,23 @@ matched = b.num_open_kprobes() + for k, v in sorted(counts.items(), key=lambda counts: counts[1].value): + if folded: + # print folded stack output +- line = k.waker.decode() + ";" ++ line = k.waker.decode('utf-8', 'replace') + ";" + for i in reversed(range(0, maxdepth)): + if k.ret[i] == 0: + continue + line = line + b.ksym(k.ret[i]) + if i != 0: + line = line + ";" +- print("%s;%s %d" % (line, k.target.decode(), v.value)) ++ print("%s;%s %d" % (line, k.target.decode('utf-8', 'replace'), v.value)) + else: + # print default multi-line stack output +- print(" %-16s %s" % ("target:", k.target.decode())) ++ print(" %-16s %s" % ("target:", k.target.decode('utf-8', 'replace'))) + for i in range(0, maxdepth): + if k.ret[i] == 0: + break + print(" %-16x %s" % (k.ret[i], + b.ksym(k.ret[i]))) +- print(" %-16s %s" % ("waker:", k.waker.decode())) ++ print(" %-16s %s" % ("waker:", k.waker.decode('utf-8', 'replace'))) + print(" %d\n" % v.value) + counts.clear() + +diff --git a/tools/oomkill.py b/tools/oomkill.py +index e831d44..0677e49 100755 +--- a/tools/oomkill.py ++++ b/tools/oomkill.py +@@ -69,8 +69,8 @@ TASK_COMM_LEN = 16 # linux/sched.h + avgline = stats.read().rstrip() + print(("%s Triggered by PID %d (\"%s\"), OOM kill of PID %d (\"%s\")" + ", %d pages, loadavg: %s") % (strftime("%H:%M:%S"), event.fpid, +- event.fcomm.decode(), event.tpid, event.tcomm.decode(), event.pages, +- avgline)) ++ event.fcomm.decode('utf-8', 'replace'), event.tpid, ++ event.tcomm.decode('utf-8', 'replace'), event.pages, avgline)) + + # initialize BPF + b = BPF(text=bpf_text) +diff --git a/tools/opensnoop.py b/tools/opensnoop.py +index a0657d4..418d47b 100755 +--- a/tools/opensnoop.py ++++ b/tools/opensnoop.py +@@ -184,7 +184,8 @@ print("%-6s %-16s %4s %3s %s" % + + print("%-6d %-16s %4d %3d %s" % + (event.id & 0xffffffff if args.tid else event.id >> 32, +- event.comm.decode(), fd_s, err, event.fname.decode())) ++ event.comm.decode('utf-8', 'replace'), fd_s, err, ++ event.fname.decode('utf-8', 'replace'))) + + # loop with callback to print_event + b["events"].open_perf_buffer(print_event, page_cnt=64) +diff --git a/tools/profile.py b/tools/profile.py +index 1530b98..d1d3d26 100755 +--- a/tools/profile.py ++++ b/tools/profile.py +@@ -268,7 +268,7 @@ b.attach_perf_event(ev_type=PerfType.SOFTWARE, + + def aksym(addr): + if args.annotations: +- return b.ksym(addr) + "_[k]" ++ return b.ksym(addr) + "_[k]".encode() + else: + return b.ksym(addr) + +@@ -320,7 +320,7 @@ need_delimiter = args.delimited and not (args.kernel_stacks_only or + line.append("[Missed Kernel Stack]") + else: + line.extend([b.ksym(addr) for addr in reversed(kernel_stack)]) +- print("%s %d" % (b";".join(line).decode(), v.value)) ++ print("%s %d" % (b";".join(line).decode('utf-8', 'replace'), v.value)) + else: + # print default multi-line stack output + if not args.user_stacks_only: +@@ -336,8 +336,8 @@ need_delimiter = args.delimited and not (args.kernel_stacks_only or + print(" [Missed User Stack]") + else: + for addr in user_stack: +- print(" %s" % b.sym(addr, k.pid).decode()) +- print(" %-16s %s (%d)" % ("-", k.name.decode(), k.pid)) ++ print(" %s" % b.sym(addr, k.pid).decode('utf-8', 'replace')) ++ print(" %-16s %s (%d)" % ("-", k.name.decode('utf-8', 'replace'), k.pid)) + print(" %d\n" % v.value) + + # check missing +diff --git a/tools/sslsniff.py b/tools/sslsniff.py +index 2e74fba..0c9f976 100755 +--- a/tools/sslsniff.py ++++ b/tools/sslsniff.py +@@ -221,8 +221,9 @@ start = 0 + " bytes lost) " + "-" * 5 + + fmt = "%-12s %-18.9f %-16s %-6d %-6d\n%s\n%s\n%s\n\n" +- print(fmt % (rw, time_s, event.comm.decode(), event.pid, event.len, s_mark, +- event.v0.decode(), e_mark)) ++ print(fmt % (rw, time_s, event.comm.decode('utf-8', 'replace'), ++ event.pid, event.len, s_mark, ++ event.v0.decode('utf-8', 'replace'), e_mark)) + + b["perf_SSL_write"].open_perf_buffer(print_event_write) + b["perf_SSL_read"].open_perf_buffer(print_event_read) +diff --git a/tools/stackcount.py b/tools/stackcount.py +index 8781879..5554014 100755 +--- a/tools/stackcount.py ++++ b/tools/stackcount.py +@@ -339,7 +339,7 @@ BPF_STACK_TRACE(stack_traces, 1024); + # print folded stack output + user_stack = list(user_stack) + kernel_stack = list(kernel_stack) +- line = [k.name.decode()] + \ ++ line = [k.name.decode('utf-8', 'replace')] + \ + [b.sym(addr, k.tgid) for addr in + reversed(user_stack)] + \ + (self.need_delimiter and ["-"] or []) + \ +diff --git a/tools/statsnoop.py b/tools/statsnoop.py +index 6fd8049..4e62ebd 100755 +--- a/tools/statsnoop.py ++++ b/tools/statsnoop.py +@@ -172,8 +172,9 @@ print("%-6s %-16s %4s %3s %s" % ("PID", "COMM", "FD", "ERR", "PATH")) + if args.timestamp: + print("%-14.9f" % (float(event.ts_ns - start_ts) / 1000000000), end="") + +- print("%-6d %-16s %4d %3d %s" % (event.pid, event.comm.decode(), +- fd_s, err, event.fname.decode())) ++ print("%-6d %-16s %4d %3d %s" % (event.pid, ++ event.comm.decode('utf-8', 'replace'), fd_s, err, ++ event.fname.decode('utf-8', 'replace'))) + + # loop with callback to print_event + b["events"].open_perf_buffer(print_event, page_cnt=64) +diff --git a/tools/tcpaccept.py b/tools/tcpaccept.py +index 044f15c..884b0c5 100755 +--- a/tools/tcpaccept.py ++++ b/tools/tcpaccept.py +@@ -239,7 +239,7 @@ TASK_COMM_LEN = 16 # linux/sched.h + start_ts = event.ts_us + print("%-9.3f" % ((float(event.ts_us) - start_ts) / 1000000), end="") + print("%-6d %-12.12s %-2d %-16s %-16s %-4d" % (event.pid, +- event.task.decode(), event.ip, ++ event.task.decode('utf-8', 'replace'), event.ip, + inet_ntop(AF_INET, pack("I", event.daddr)), + inet_ntop(AF_INET, pack("I", event.saddr)), event.lport)) + +@@ -251,8 +251,9 @@ TASK_COMM_LEN = 16 # linux/sched.h + start_ts = event.ts_us + print("%-9.3f" % ((float(event.ts_us) - start_ts) / 1000000), end="") + print("%-6d %-12.12s %-2d %-16s %-16s %-4d" % (event.pid, +- event.task.decode(), event.ip, inet_ntop(AF_INET6, event.daddr), +- inet_ntop(AF_INET6, event.saddr), event.lport)) ++ event.task.decode('utf-8', 'replace'), event.ip, ++ inet_ntop(AF_INET6, event.daddr),inet_ntop(AF_INET6, event.saddr), ++ event.lport)) + + # initialize BPF + b = BPF(text=bpf_text) +diff --git a/tools/tcpconnect.py b/tools/tcpconnect.py +index f0b23b0..ac84326 100755 +--- a/tools/tcpconnect.py ++++ b/tools/tcpconnect.py +@@ -202,7 +202,7 @@ TASK_COMM_LEN = 16 # linux/sched.h + start_ts = event.ts_us + print("%-9.3f" % ((float(event.ts_us) - start_ts) / 1000000), end="") + print("%-6d %-12.12s %-2d %-16s %-16s %-4d" % (event.pid, +- event.task.decode(), event.ip, ++ event.task.decode('utf-8', 'replace'), event.ip, + inet_ntop(AF_INET, pack("I", event.saddr)), + inet_ntop(AF_INET, pack("I", event.daddr)), event.dport)) + +@@ -214,8 +214,9 @@ TASK_COMM_LEN = 16 # linux/sched.h + start_ts = event.ts_us + print("%-9.3f" % ((float(event.ts_us) - start_ts) / 1000000), end="") + print("%-6d %-12.12s %-2d %-16s %-16s %-4d" % (event.pid, +- event.task.decode(), event.ip, inet_ntop(AF_INET6, event.saddr), +- inet_ntop(AF_INET6, event.daddr), event.dport)) ++ event.task.decode('utf-8', 'replace'), event.ip, ++ inet_ntop(AF_INET6, event.saddr), inet_ntop(AF_INET6, event.daddr), ++ event.dport)) + + # initialize BPF + b = BPF(text=bpf_text) +diff --git a/tools/tcpconnlat.py b/tools/tcpconnlat.py +index 233612b..0d21b83 100755 +--- a/tools/tcpconnlat.py ++++ b/tools/tcpconnlat.py +@@ -237,7 +237,7 @@ start_ts = 0 + start_ts = event.ts_us + print("%-9.3f" % ((float(event.ts_us) - start_ts) / 1000000), end="") + print("%-6d %-12.12s %-2d %-16s %-16s %-5d %.2f" % (event.pid, +- event.task.decode(), event.ip, ++ event.task.decode('utf-8', 'replace'), event.ip, + inet_ntop(AF_INET, pack("I", event.saddr)), + inet_ntop(AF_INET, pack("I", event.daddr)), event.dport, + float(event.delta_us) / 1000)) +@@ -250,9 +250,9 @@ start_ts = 0 + start_ts = event.ts_us + print("%-9.3f" % ((float(event.ts_us) - start_ts) / 1000000), end="") + print("%-6d %-12.12s %-2d %-16s %-16s %-5d %.2f" % (event.pid, +- event.task.decode(), event.ip, inet_ntop(AF_INET6, event.saddr), +- inet_ntop(AF_INET6, event.daddr), event.dport, +- float(event.delta_us) / 1000)) ++ event.task.decode('utf-8', 'replace'), event.ip, ++ inet_ntop(AF_INET6, event.saddr), inet_ntop(AF_INET6, event.daddr), ++ event.dport, float(event.delta_us) / 1000)) + + # header + if args.timestamp: +diff --git a/tools/tcplife.py b/tools/tcplife.py +index f8bab43..51ed7ae 100755 +--- a/tools/tcplife.py ++++ b/tools/tcplife.py +@@ -454,7 +454,7 @@ format_string = "%-5d %-10.10s %s%-15s %-5d %-15s %-5d %5d %5d %.2f" + print("%.6f," % delta_s, end="") + else: + print("%-9.6f " % delta_s, end="") +- print(format_string % (event.pid, event.task.decode(), ++ print(format_string % (event.pid, event.task.decode('utf-8', 'replace'), + "4" if args.wide or args.csv else "", + inet_ntop(AF_INET, pack("I", event.saddr)), event.ports >> 32, + inet_ntop(AF_INET, pack("I", event.daddr)), event.ports & 0xffffffff, +@@ -476,7 +476,7 @@ format_string = "%-5d %-10.10s %s%-15s %-5d %-15s %-5d %5d %5d %.2f" + print("%.6f," % delta_s, end="") + else: + print("%-9.6f " % delta_s, end="") +- print(format_string % (event.pid, event.task.decode(), ++ print(format_string % (event.pid, event.task.decode('utf-8', 'replace'), + "6" if args.wide or args.csv else "", + inet_ntop(AF_INET6, event.saddr), event.ports >> 32, + inet_ntop(AF_INET6, event.daddr), event.ports & 0xffffffff, +diff --git a/tools/tcpstates.py b/tools/tcpstates.py +index ec758d2..381a6d5 100755 +--- a/tools/tcpstates.py ++++ b/tools/tcpstates.py +@@ -276,7 +276,7 @@ format_string = ("%-16x %-5d %-10.10s %s%-15s %-5d %-15s %-5d %-11s " + + print("%.6f," % delta_s, end="") + else: + print("%-9.6f " % delta_s, end="") +- print(format_string % (event.skaddr, event.pid, event.task.decode(), ++ print(format_string % (event.skaddr, event.pid, event.task.decode('utf-8', 'replace'), + "4" if args.wide or args.csv else "", + inet_ntop(AF_INET, pack("I", event.saddr)), event.ports >> 32, + inet_ntop(AF_INET, pack("I", event.daddr)), event.ports & 0xffffffff, +@@ -299,7 +299,7 @@ format_string = ("%-16x %-5d %-10.10s %s%-15s %-5d %-15s %-5d %-11s " + + print("%.6f," % delta_s, end="") + else: + print("%-9.6f " % delta_s, end="") +- print(format_string % (event.skaddr, event.pid, event.task.decode(), ++ print(format_string % (event.skaddr, event.pid, event.task.decode('utf-8', 'replace'), + "6" if args.wide or args.csv else "", + inet_ntop(AF_INET6, event.saddr), event.ports >> 32, + inet_ntop(AF_INET6, event.daddr), event.ports & 0xffffffff, +diff --git a/tools/tcptracer.py b/tools/tcptracer.py +index 5e97ee6..16bb4b1 100755 +--- a/tools/tcptracer.py ++++ b/tools/tcptracer.py +@@ -556,7 +556,7 @@ verbose_types = {"C": "connect", "A": "accept", + print("%-2s " % (type_str), end="") + + print("%-6d %-16s %-2d %-16s %-16s %-6d %-6d" % +- (event.pid, event.comm.decode('utf-8'), ++ (event.pid, event.comm.decode('utf-8', 'replace'), + event.ip, + inet_ntop(AF_INET, pack("I", event.saddr)), + inet_ntop(AF_INET, pack("I", event.daddr)), +@@ -593,7 +593,7 @@ verbose_types = {"C": "connect", "A": "accept", + print("%-2s " % (type_str), end="") + + print("%-6d %-16s %-2d %-16s %-16s %-6d %-6d" % +- (event.pid, event.comm.decode('utf-8'), ++ (event.pid, event.comm.decode('utf-8', 'replace'), + event.ip, + "[" + inet_ntop(AF_INET6, event.saddr) + "]", + "[" + inet_ntop(AF_INET6, event.daddr) + "]", +diff --git a/tools/trace.py b/tools/trace.py +index 16e9b6b..2233305 100755 +--- a/tools/trace.py ++++ b/tools/trace.py +@@ -558,7 +558,8 @@ BPF_PERF_OUTPUT(%s); + if Probe.print_cpu: + print("%-3s " % event.cpu, end="") + print("%-7d %-7d %-15s %-16s %s" % +- (event.tgid, event.pid, event.comm.decode(), ++ (event.tgid, event.pid, ++ event.comm.decode('utf-8', 'replace'), + self._display_function(), msg)) + + if self.kernel_stack: +diff --git a/tools/ttysnoop.py b/tools/ttysnoop.py +index e934486..9780518 100755 +--- a/tools/ttysnoop.py ++++ b/tools/ttysnoop.py +@@ -115,7 +115,7 @@ BUFSIZE = 256 + # process event + def print_event(cpu, data, size): + event = ct.cast(data, ct.POINTER(Data)).contents +- print("%s" % event.buf[0:event.count].decode(), end="") ++ print("%s" % event.buf[0:event.count].decode('utf-8', 'replace'), end="") + sys.stdout.flush() + + # loop with callback to print_event +diff --git a/tools/zfsslower.py b/tools/zfsslower.py +index 6de4606..8ab283a 100755 +--- a/tools/zfsslower.py ++++ b/tools/zfsslower.py +@@ -265,12 +265,14 @@ TASK_COMM_LEN = 16 # linux/sched.h + + if (csv): + print("%d,%s,%d,%s,%d,%d,%d,%s" % ( +- event.ts_us, event.task.decode(), event.pid, type, event.size, +- event.offset, event.delta_us, event.file.decode())) ++ event.ts_us, event.task.decode('utf-8', 'replace'), event.pid, ++ type, event.size, event.offset, event.delta_us, ++ event.file.decode('utf-8', 'replace'))) + return + print("%-8s %-14.14s %-6s %1s %-7s %-8d %7.2f %s" % (strftime("%H:%M:%S"), +- event.task.decode(), event.pid, type, event.size, event.offset / 1024, +- float(event.delta_us) / 1000, event.file.decode())) ++ event.task.decode('utf-8', 'replace'), event.pid, type, event.size, ++ event.offset / 1024, float(event.delta_us) / 1000, ++ event.file.decode('utf-8', 'replace'))) + + # initialize BPF + b = BPF(text=bpf_text) +-- +2.17.2 + diff --git a/SOURCES/Disable-tests-cc.patch b/SOURCES/Disable-tests-cc.patch new file mode 100644 index 0000000..8661bf3 --- /dev/null +++ b/SOURCES/Disable-tests-cc.patch @@ -0,0 +1,28 @@ +From b3faa19c61a5a5385899a80bdd0d6a3052f1415a Mon Sep 17 00:00:00 2001 +From: Jerome Marchand +Date: Thu, 24 May 2018 16:11:30 +0200 +Subject: [PATCH] Disable tests/cc + +Some files in tests/cc/ don't compile on s390x. We can just disable +them until a better fix is available: the build target are not +installed anyway. + +--- + tests/CMakeLists.txt | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt +index 86abec9..48e9029 100644 +--- a/tests/CMakeLists.txt ++++ b/tests/CMakeLists.txt +@@ -8,7 +8,6 @@ add_test(NAME style-check COMMAND ${CMAKE_SOURCE_DIR}/scripts/style-check.sh) + set_tests_properties(style-check PROPERTIES PASS_REGULAR_EXPRESSION ".*") + + if(ENABLE_CLANG_JIT) +-add_subdirectory(cc) + add_subdirectory(python) + add_subdirectory(lua) + endif() +-- +2.13.6 + diff --git a/SOURCES/Tools-fixes-2009.patch b/SOURCES/Tools-fixes-2009.patch new file mode 100644 index 0000000..95c5489 --- /dev/null +++ b/SOURCES/Tools-fixes-2009.patch @@ -0,0 +1,69 @@ +From 09f9d3c8dd016ee10dc8b57057763dc25b0d2208 Mon Sep 17 00:00:00 2001 +From: jeromemarchand <38073585+jeromemarchand@users.noreply.github.com> +Date: Sat, 13 Oct 2018 01:01:22 +0200 +Subject: [PATCH] Tools fixes (#2009) + +* tools: the argument of get_kprobe_functions() should be a bytes object + +It fixes the following error: +Traceback (most recent call last): + File "/usr/share/bcc/tools/ext4dist", line 189, in + if BPF.get_kprobe_functions('ext4_file_read_iter'): + File "/usr/lib/python3.6/site-packages/bcc/__init__.py", line 519, in get_kprobe_functions + if (t.lower() in [b't', b'w']) and re.match(event_re, fn) \ + File "/usr/lib64/python3.6/re.py", line 172, in match + return _compile(pattern, flags).match(string) +TypeError: cannot use a string pattern on a bytes-like object + +* tools: fix freeze in offwaketime + +Since commit 47cecb65ce6e, the sigint signal is ignored +unconditionally which prevents offwaketime to be stopped by +pressing Ctrl-C when run without a specified duration. +--- + tools/ext4dist.py | 4 ++-- + tools/offwaketime.py | 10 ++++++---- + 2 files changed, 8 insertions(+), 6 deletions(-) + +diff --git a/tools/ext4dist.py b/tools/ext4dist.py +index 1806953..bc797fb 100755 +--- a/tools/ext4dist.py ++++ b/tools/ext4dist.py +@@ -183,10 +183,10 @@ b = BPF(text=bpf_text) + # Comment by Joe Yin + # From Linux 4.10, the function .read_iter at the ext4_file_operations has + # changed to ext4_file_read_iter. +-# So, I add get_kprobe_functions('ext4_file_read_iter'),it will first to attach ext4_file_read_iter, ++# So, I add get_kprobe_functions(b'ext4_file_read_iter'),it will first to attach ext4_file_read_iter, + # if fails and will attach the generic_file_read_iter which used to pre-4.10. + +-if BPF.get_kprobe_functions('ext4_file_read_iter'): ++if BPF.get_kprobe_functions(b'ext4_file_read_iter'): + b.attach_kprobe(event="ext4_file_read_iter", fn_name="trace_entry") + else: + b.attach_kprobe(event="generic_file_read_iter", fn_name="trace_read_entry") +diff --git a/tools/offwaketime.py b/tools/offwaketime.py +index 674be22..0e4f35e 100755 +--- a/tools/offwaketime.py ++++ b/tools/offwaketime.py +@@ -277,11 +277,13 @@ matched = b.num_open_kprobes() + else: + print("... Hit Ctrl-C to end.") + +-# as cleanup can take many seconds, trap Ctrl-C: +-# print a newline for folded output on Ctrl-C +-signal.signal(signal.SIGINT, signal_ignore) ++try: ++ sleep(duration) ++except KeyboardInterrupt: ++ # as cleanup can take many seconds, trap Ctrl-C: ++ # print a newline for folded output on Ctrl-C ++ signal.signal(signal.SIGINT, signal_ignore) + +-sleep(duration) + + if not folded: + print() +-- +2.17.2 + diff --git a/SOURCES/biolatency-Fix-disks-bpf_probe_read-1980.patch b/SOURCES/biolatency-Fix-disks-bpf_probe_read-1980.patch new file mode 100644 index 0000000..5008511 --- /dev/null +++ b/SOURCES/biolatency-Fix-disks-bpf_probe_read-1980.patch @@ -0,0 +1,48 @@ +From fc245dfe9dabbd77e3adbf89a45aaab0cb1552fd Mon Sep 17 00:00:00 2001 +From: Mark Drayton +Date: Wed, 19 Sep 2018 16:41:49 -0700 +Subject: [PATCH] biolatency: Fix --disks bpf_probe_read() (#1980) + +bpf_probe_read()'s third argument is no longer rewritten. Instead, use a +temporary variable (like #1973) to avoid a memory access error. + +Before: + +``` +$ sudo biolatency -D 1 +bpf: Failed to load program: Permission denied +0: (79) r1 = *(u64 *)(r1 +112) +1: (7b) *(u64 *)(r10 -8) = r1 +[..] +R1 invalid mem access 'inv' + +HINT: The invalid mem access 'inv' error can happen if you try to +dereference memory without first using bpf_probe_read() to copy it to +the BPF stack. Sometimes the bpf_probe_read is automatic by the bcc +rewriter, other times you'll need to be explicit. +``` + +After, works as expected. +--- + tools/biolatency.py | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +diff --git a/tools/biolatency.py b/tools/biolatency.py +index 49ee7cf..3879af1 100755 +--- a/tools/biolatency.py ++++ b/tools/biolatency.py +@@ -99,8 +99,9 @@ int trace_req_completion(struct pt_regs *ctx, struct request *req) + 'BPF_HISTOGRAM(dist, disk_key_t);') + bpf_text = bpf_text.replace('STORE', + 'disk_key_t key = {.slot = bpf_log2l(delta)}; ' + +- 'bpf_probe_read(&key.disk, sizeof(key.disk), ' + +- 'req->rq_disk->disk_name); dist.increment(key);') ++ 'void *__tmp = (void *)req->rq_disk->disk_name; ' + ++ 'bpf_probe_read(&key.disk, sizeof(key.disk), __tmp); ' + ++ 'dist.increment(key);') + else: + bpf_text = bpf_text.replace('STORAGE', 'BPF_HISTOGRAM(dist);') + bpf_text = bpf_text.replace('STORE', +-- +2.19.2 + diff --git a/SOURCES/covscan-fix-miscellaneaous-errors-2003.patch b/SOURCES/covscan-fix-miscellaneaous-errors-2003.patch new file mode 100644 index 0000000..f5adf5f --- /dev/null +++ b/SOURCES/covscan-fix-miscellaneaous-errors-2003.patch @@ -0,0 +1,153 @@ +From 415bd4e43b2c27e3999923c16f5ff39f9b1adcae Mon Sep 17 00:00:00 2001 +From: Jerome Marchand +Date: Thu, 1 Nov 2018 06:18:14 +0100 +Subject: [PATCH] covscan: fix miscellaneaous errors (#2003) + +* Coverity #def53: COPY_PASTE_ERROR + +* Coverity #def18: DC.STREAM_BUFFER. Double-check max length of dev + +* Coverity #def44: MISSING_BREAK. This looks like it should be here + +* Coverity #def67: STRING_NULL: potential OOB read if 0 bytes read. + +* Coverity #def66: FORWARD_NULL: potential null ptr deref + +* Coverity #def17: RESOURCE_LEAK: missing free() + +* Dont free the result of dirname + +dirname() may return pointers to statically allocated memory. Don't +free the pointer it returns. +--- + src/cc/bcc_elf.c | 11 +++++++---- + src/cc/bcc_proc.c | 4 ++-- + src/cc/frontends/b/type_check.cc | 1 + + src/cc/frontends/p4/compiler/ebpfTable.py | 2 +- + src/cc/libbpf.c | 20 +++++++++++--------- + 5 files changed, 22 insertions(+), 16 deletions(-) + +diff --git a/src/cc/bcc_elf.c b/src/cc/bcc_elf.c +index c425db6..0c696bd 100644 +--- a/src/cc/bcc_elf.c ++++ b/src/cc/bcc_elf.c +@@ -398,6 +398,7 @@ static int verify_checksum(const char *file, unsigned int crc) { + static char *find_debug_via_debuglink(Elf *e, const char *binpath, + int check_crc) { + char fullpath[PATH_MAX]; ++ char *tmppath; + char *bindir = NULL; + char *res = NULL; + unsigned int crc; +@@ -406,8 +407,8 @@ static char *find_debug_via_debuglink(Elf *e, const char *binpath, + if (!find_debuglink(e, &name, &crc)) + return NULL; + +- bindir = strdup(binpath); +- bindir = dirname(bindir); ++ tmppath = strdup(binpath); ++ bindir = dirname(tmppath); + + // Search for the file in 'binpath', but ignore the file we find if it + // matches the binary itself: the binary will always be probed later on, +@@ -434,9 +435,11 @@ static char *find_debug_via_debuglink(Elf *e, const char *binpath, + } + + DONE: +- free(bindir); +- if (res && check_crc && !verify_checksum(res, crc)) ++ free(tmppath); ++ if (res && check_crc && !verify_checksum(res, crc)) { ++ free(res); + return NULL; ++ } + return res; + } + +diff --git a/src/cc/bcc_proc.c b/src/cc/bcc_proc.c +index d694eb9..f1c30c2 100644 +--- a/src/cc/bcc_proc.c ++++ b/src/cc/bcc_proc.c +@@ -92,14 +92,14 @@ int bcc_procutils_each_module(int pid, bcc_procutils_modulecb callback, + if (!procmap) + return -1; + +- char buf[PATH_MAX + 1], perm[5], dev[8]; ++ char buf[PATH_MAX + 1], perm[5], dev[6]; + char *name; + uint64_t begin, end, inode; + unsigned long long offset; + while (true) { + buf[0] = '\0'; + // From fs/proc/task_mmu.c:show_map_vma +- if (fscanf(procmap, "%lx-%lx %s %llx %s %lu%[^\n]", &begin, &end, perm, ++ if (fscanf(procmap, "%lx-%lx %4s %llx %5s %lu%[^\n]", &begin, &end, perm, + &offset, dev, &inode, buf) != 7) + break; + +diff --git a/src/cc/frontends/b/type_check.cc b/src/cc/frontends/b/type_check.cc +index 8d49de9..7c5b7ce 100644 +--- a/src/cc/frontends/b/type_check.cc ++++ b/src/cc/frontends/b/type_check.cc +@@ -204,6 +204,7 @@ StatusTuple TypeCheck::visit_binop_expr_node(BinopExprNode *n) { + case Tok::TCGT: + case Tok::TCGE: + n->bit_width_ = 1; ++ break; + default: + n->bit_width_ = std::max(n->lhs_->bit_width_, n->rhs_->bit_width_); + } +diff --git a/src/cc/frontends/p4/compiler/ebpfTable.py b/src/cc/frontends/p4/compiler/ebpfTable.py +index eb1efd9..4b7e023 100644 +--- a/src/cc/frontends/p4/compiler/ebpfTable.py ++++ b/src/cc/frontends/p4/compiler/ebpfTable.py +@@ -110,7 +110,7 @@ import ebpfAction + ebpfHeader = program.getInstance(instance.name) + assert isinstance(ebpfHeader, ebpfInstance.SimpleInstance) + basetype = ebpfHeader.type +- eInstance = program.getInstance(instance.base_name) ++ eInstance = program.getInstance(instance.name) + + ebpfField = basetype.getField(fieldname) + assert isinstance(ebpfField, ebpfStructType.EbpfField) +diff --git a/src/cc/libbpf.c b/src/cc/libbpf.c +index 8a7caec..5cf3554 100644 +--- a/src/cc/libbpf.c ++++ b/src/cc/libbpf.c +@@ -521,14 +521,16 @@ int bpf_prog_load(enum bpf_prog_type prog_type, const char *name, + } + } + +- if (strncmp(name, "kprobe__", 8) == 0) +- name_offset = 8; +- else if (strncmp(name, "tracepoint__", 12) == 0) +- name_offset = 12; +- else if (strncmp(name, "raw_tracepoint__", 16) == 0) +- name_offset = 16; +- memcpy(attr.prog_name, name + name_offset, +- min(name_len - name_offset, BPF_OBJ_NAME_LEN - 1)); ++ if (name_len) { ++ if (strncmp(name, "kprobe__", 8) == 0) ++ name_offset = 8; ++ else if (strncmp(name, "tracepoint__", 12) == 0) ++ name_offset = 12; ++ else if (strncmp(name, "raw_tracepoint__", 16) == 0) ++ name_offset = 16; ++ memcpy(attr.prog_name, name + name_offset, ++ min(name_len - name_offset, BPF_OBJ_NAME_LEN - 1)); ++ } + + ret = syscall(__NR_bpf, BPF_PROG_LOAD, &attr, sizeof(attr)); + // BPF object name is not supported on older Kernels. +@@ -698,7 +700,7 @@ static int bpf_get_retprobe_bit(const char *event_type) + close(fd); + if (ret < 0 || ret >= sizeof(buf)) + return -1; +- if (strlen(buf) < strlen("config:")) ++ if (strncmp(buf, "config:", strlen("config:"))) + return -1; + errno = 0; + ret = (int)strtol(buf + strlen("config:"), NULL, 10); +-- +2.17.2 + diff --git a/SOURCES/fix-killsnoop.py-srr-bytes-type-error-2007.patch b/SOURCES/fix-killsnoop.py-srr-bytes-type-error-2007.patch new file mode 100644 index 0000000..e712d6b --- /dev/null +++ b/SOURCES/fix-killsnoop.py-srr-bytes-type-error-2007.patch @@ -0,0 +1,28 @@ +From 1d2348027404307b6cd51c0c452207be27c64863 Mon Sep 17 00:00:00 2001 +From: yonghong-song +Date: Thu, 11 Oct 2018 08:32:04 -0700 +Subject: [PATCH] fix killsnoop.py srr/bytes type error (#2007) + +Fix issue #2002 + +Signed-off-by: Yonghong Song +--- + tools/killsnoop.py | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/tools/killsnoop.py b/tools/killsnoop.py +index 89ea5cd..d60c72e 100755 +--- a/tools/killsnoop.py ++++ b/tools/killsnoop.py +@@ -139,7 +139,7 @@ print("%-9s %-6s %-16s %-4s %-6s %s" % ( + if (args.failed and (event.ret >= 0)): + return + +- printb(b"%-9s %-6d %-16s %-4d %-6d %d" % (strftime("%H:%M:%S"), ++ printb(b"%-9s %-6d %-16s %-4d %-6d %d" % (strftime("%H:%M:%S").encode('ascii'), + event.pid, event.comm, event.sig, event.tpid, event.ret)) + + # loop with callback to print_event +-- +2.17.2 + diff --git a/SOURCES/manpage-remove-non-existent-p-option-from-the-biotop.patch b/SOURCES/manpage-remove-non-existent-p-option-from-the-biotop.patch new file mode 100644 index 0000000..1b64154 --- /dev/null +++ b/SOURCES/manpage-remove-non-existent-p-option-from-the-biotop.patch @@ -0,0 +1,30 @@ +From b29ede1b052b5706581370ee98d2eccfbf700e77 Mon Sep 17 00:00:00 2001 +From: Jerome Marchand +Date: Wed, 12 Dec 2018 10:50:08 +0100 +Subject: [PATCH] manpage: remove non-existent -p option from the biotop + manpage + +The biotop manpage references a -p option that the tools doesn't have, +and AFAICT, never had. It's only referenced in the manpage option, not +in the synopsis, in "biotop -h" output not biotop_example.txt. +--- + man/man8/biotop.8 | 3 --- + 1 file changed, 3 deletions(-) + +diff --git a/man/man8/biotop.8 b/man/man8/biotop.8 +index 8b872aa..ed25521 100644 +--- a/man/man8/biotop.8 ++++ b/man/man8/biotop.8 +@@ -30,9 +30,6 @@ Don't clear the screen. + \-r MAXROWS + Maximum number of rows to print. Default is 20. + .TP +-\-p PID +-Trace this PID only. +-.TP + interval + Interval between updates, seconds. + .TP +-- +2.19.2 + diff --git a/SOURCES/tools-prepare-block-tools-for-the-removing-of-legacy.patch b/SOURCES/tools-prepare-block-tools-for-the-removing-of-legacy.patch new file mode 100644 index 0000000..4416269 --- /dev/null +++ b/SOURCES/tools-prepare-block-tools-for-the-removing-of-legacy.patch @@ -0,0 +1,60 @@ +From 74e25edb248a62e437cdc1b4aaa8a0f44c112880 Mon Sep 17 00:00:00 2001 +From: Jerome Marchand +Date: Mon, 10 Dec 2018 08:54:50 +0100 +Subject: [PATCH] tools: prepare block tools for the removing of legacy I/O + path (#2070) + +Recent -next kernels don't have blk_start_request() function +anymore. It has been removed in a recent cleanup. bio* tools should be +able to handle the lack of this probe. +--- + tools/biolatency.py | 3 ++- + tools/biosnoop.py | 3 ++- + tools/biotop.py | 3 ++- + 3 files changed, 6 insertions(+), 3 deletions(-) + +diff --git a/tools/biolatency.py b/tools/biolatency.py +index 3879af1..68aa577 100755 +--- a/tools/biolatency.py ++++ b/tools/biolatency.py +@@ -116,7 +116,8 @@ b = BPF(text=bpf_text) + if args.queued: + b.attach_kprobe(event="blk_account_io_start", fn_name="trace_req_start") + else: +- b.attach_kprobe(event="blk_start_request", fn_name="trace_req_start") ++ if BPF.get_kprobe_functions(b'blk_start_request'): ++ b.attach_kprobe(event="blk_start_request", fn_name="trace_req_start") + b.attach_kprobe(event="blk_mq_start_request", fn_name="trace_req_start") + b.attach_kprobe(event="blk_account_io_completion", + fn_name="trace_req_completion") +diff --git a/tools/biosnoop.py b/tools/biosnoop.py +index 2b1e77d..d036c0b 100755 +--- a/tools/biosnoop.py ++++ b/tools/biosnoop.py +@@ -122,7 +122,8 @@ int trace_req_completion(struct pt_regs *ctx, struct request *req) + } + """, debug=0) + b.attach_kprobe(event="blk_account_io_start", fn_name="trace_pid_start") +-b.attach_kprobe(event="blk_start_request", fn_name="trace_req_start") ++if BPF.get_kprobe_functions(b'blk_start_request'): ++ b.attach_kprobe(event="blk_start_request", fn_name="trace_req_start") + b.attach_kprobe(event="blk_mq_start_request", fn_name="trace_req_start") + b.attach_kprobe(event="blk_account_io_completion", + fn_name="trace_req_completion") +diff --git a/tools/biotop.py b/tools/biotop.py +index c6e1ca2..b2b5089 100755 +--- a/tools/biotop.py ++++ b/tools/biotop.py +@@ -173,7 +173,8 @@ int trace_req_completion(struct pt_regs *ctx, struct request *req) + + b = BPF(text=bpf_text) + b.attach_kprobe(event="blk_account_io_start", fn_name="trace_pid_start") +-b.attach_kprobe(event="blk_start_request", fn_name="trace_req_start") ++if BPF.get_kprobe_functions(b'blk_start_request'): ++ b.attach_kprobe(event="blk_start_request", fn_name="trace_req_start") + b.attach_kprobe(event="blk_mq_start_request", fn_name="trace_req_start") + b.attach_kprobe(event="blk_account_io_completion", + fn_name="trace_req_completion") +-- +2.19.2 + diff --git a/SOURCES/uflow-str-bytes.patch b/SOURCES/uflow-str-bytes.patch new file mode 100644 index 0000000..e28b98e --- /dev/null +++ b/SOURCES/uflow-str-bytes.patch @@ -0,0 +1,65 @@ +From 215fc84bac0037ac5b2047940f97ecc97f0860b9 Mon Sep 17 00:00:00 2001 +From: Marko Myllynen +Date: Fri, 5 Oct 2018 16:47:29 +0300 +Subject: [PATCH 1/2] uflow: convert bytes to str + +Python 3 fix, similar to commit 99d1468 and commit 4e4c9e0 for ucalls. + +Closes #1996. +--- + tools/lib/uflow.py | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/tools/lib/uflow.py b/tools/lib/uflow.py +index fb306e31d..fdbcfc332 100755 +--- a/tools/lib/uflow.py ++++ b/tools/lib/uflow.py +@@ -196,7 +196,9 @@ def print_event(cpu, data, size): + direction = "<- " if event.depth & (1 << 63) else "-> " + print("%-3d %-6d %-6d %-8.3f %-40s" % (cpu, event.pid >> 32, + event.pid & 0xFFFFFFFF, time.time() - start_ts, +- (" " * (depth - 1)) + direction + event.clazz + "." + event.method)) ++ (" " * (depth - 1)) + direction + \ ++ event.clazz.decode('utf-8', 'replace') + "." + \ ++ event.method.decode('utf-8', 'replace'))) + + bpf["calls"].open_perf_buffer(print_event) + while 1: + +From 1c7e2a8f3fb02d4516fccc410272324fff250be9 Mon Sep 17 00:00:00 2001 +From: Marko Myllynen +Date: Fri, 5 Oct 2018 17:16:13 +0300 +Subject: [PATCH 2/2] uflow: drop unused timestamp field + +--- + tools/lib/uflow.py | 3 --- + 1 file changed, 3 deletions(-) + +diff --git a/tools/lib/uflow.py b/tools/lib/uflow.py +index fdbcfc332..f2a458d7b 100755 +--- a/tools/lib/uflow.py ++++ b/tools/lib/uflow.py +@@ -49,7 +49,6 @@ + struct call_t { + u64 depth; // first bit is direction (0 entry, 1 return) + u64 pid; // (tgid << 32) + pid from bpf_get_current... +- u64 timestamp; // ns + char clazz[80]; + char method[80]; + }; +@@ -89,7 +88,6 @@ + FILTER_METHOD + + data.pid = bpf_get_current_pid_tgid(); +- data.timestamp = bpf_ktime_get_ns(); + depth = entry.lookup_or_init(&data.pid, &zero); + data.depth = DEPTH; + UPDATE +@@ -183,7 +181,6 @@ class CallEvent(ct.Structure): + _fields_ = [ + ("depth", ct.c_ulonglong), + ("pid", ct.c_ulonglong), +- ("timestamp", ct.c_ulonglong), + ("clazz", ct.c_char * 80), + ("method", ct.c_char * 80) + ] diff --git a/SPECS/bcc.spec b/SPECS/bcc.spec new file mode 100644 index 0000000..a815764 --- /dev/null +++ b/SPECS/bcc.spec @@ -0,0 +1,302 @@ +# luajit is not available RHEL 8 +%bcond_with lua + +%bcond_without llvm_static + +Name: bcc +Version: 0.7.0 +Release: 5%{?dist} +Summary: BPF Compiler Collection (BCC) +License: ASL 2.0 +URL: https://github.com/iovisor/bcc +Source0: %{url}/archive/v%{version}/%{name}-%{version}.tar.gz +Patch0: uflow-str-bytes.patch +Patch1: fix-killsnoop.py-srr-bytes-type-error-2007.patch +Patch2: Tools-fixes-2009.patch +Patch3: Bytes-string-encoding-2004.patch +Patch4: covscan-fix-miscellaneaous-errors-2003.patch +Patch5: tools-prepare-block-tools-for-the-removing-of-legacy.patch +Patch6: biolatency-Fix-disks-bpf_probe_read-1980.patch +Patch7: manpage-remove-non-existent-p-option-from-the-biotop.patch +# tests/cc doesn't compile on s390x, so disable it until we have a better fix +Patch10: Disable-tests-cc.patch + +# Arches will be included as upstream support is added and dependencies are +# satisfied in the respective arches +ExcludeArch: i686 + +BuildRequires: bison, cmake >= 2.8.7, flex, libxml2-devel +BuildRequires: python3-devel +BuildRequires: elfutils-libelf-devel +BuildRequires: llvm-devel +BuildRequires: clang-devel +BuildRequires: ncurses-devel +%if %{with lua} +BuildRequires: pkgconfig(luajit) +%endif +%if %{with llvm_static} +BuildRequires: llvm-static +%endif + +BuildRequires: clang + +Requires: %{name}-tools = %{version}-%{release} + +%description +BCC is a toolkit for creating efficient kernel tracing and manipulation +programs, and includes several useful tools and examples. It makes use of +extended BPF (Berkeley Packet Filters), formally known as eBPF, a new feature +that was first added to Linux 3.15. BCC makes BPF programs easier to write, +with kernel instrumentation in C (and includes a C wrapper around LLVM), and +front-ends in Python and lua. It is suited for many tasks, including +performance analysis and network traffic control. + + +%package devel +Summary: Shared library for BPF Compiler Collection (BCC) +Requires: %{name}%{?_isa} = %{version}-%{release} + +%description devel +The %{name}-devel package contains libraries and header files for developing +application that use BPF Compiler Collection (BCC). + + +%package doc +Summary: Examples for BPF Compiler Collection (BCC) +Recommends: python3-%{name} = %{version}-%{release} +%if %{with lua} +Recommends: %{name}-lua = %{version}-%{release} +%endif +BuildArch: noarch + +%description doc +Examples for BPF Compiler Collection (BCC) + + +%package -n python3-%{name} +Summary: Python3 bindings for BPF Compiler Collection (BCC) +Requires: %{name}%{?_isa} = %{version}-%{release} +%{?python_provide:%python_provide python3-%{srcname}} + +%description -n python3-%{name} +Python3 bindings for BPF Compiler Collection (BCC) + + +%if %{with lua} +%package lua +Summary: Standalone tool to run BCC tracers written in Lua +Requires: %{name}%{?_isa} = %{version}-%{release} + +%description lua +Standalone tool to run BCC tracers written in Lua +%endif + + +%package tools +Summary: Command line tools for BPF Compiler Collection (BCC) +Requires: python3-%{name} = %{version}-%{release} +Requires: python3-netaddr +Requires: kernel-devel + +%description tools +Command line tools for BPF Compiler Collection (BCC) + +%prep +%setup + +%patch0 -p1 +%patch1 -p1 +%patch2 -p1 +%patch3 -p1 +%patch4 -p1 +%patch5 -p1 +%patch6 -p1 +%patch7 -p1 + +%ifarch s390x +%patch10 -p1 +%endif + +%build +%cmake . \ + -DCMAKE_BUILD_TYPE=RelWithDebInfo \ + -DREVISION_LAST=%{version} -DREVISION=%{version} -DPYTHON_CMD=python3 +%make_build + +%install +%make_install + +# Fix python shebangs +# This messes the timestamp and rpmdiff complains about it +# Let's set the all thing according to a reference file +touch -r %{buildroot}%{_datadir}/%{name}/examples/hello_world.py %{_tmppath}/timestamp + +find %{buildroot}%{_datadir}/%{name}/{tools,examples} -type f -exec \ + sed -i -e '1s=^#!/usr/bin/python\([0-9.]\+\)\?$=#!%{__python3}=' \ + -e '1s=^#!/usr/bin/env python\([0-9.]\+\)\?$=#!%{__python3}=' \ + -e '1s=^#!/usr/bin/env bcc-lua$=#!/usr/bin/bcc-lua=' {} \; + +for i in `find %{buildroot}%{_datadir}/%{name}/examples/` ; do + touch -h -r %{_tmppath}/timestamp $i +done + +# Move man pages to the right location +mkdir -p %{buildroot}%{_mandir} +mv %{buildroot}%{_datadir}/%{name}/man/* %{buildroot}%{_mandir}/ +# Avoid conflict with other manpages +# https://bugzilla.redhat.com/show_bug.cgi?id=1517408 +for i in `find %{buildroot}%{_mandir} -name "*.gz"`; do + tname=$(basename $i) + rename $tname %{name}-$tname $i +done +# Fix the symlink too +for i in `find %{buildroot}%{_mandir} -lname \*.gz` ; do + target=`readlink $i`; + ln -sf bcc-$target $i; +done + +# We cannot run the test suit since it requires root and it makes changes to +# the machine (e.g, IP address) +#%check + +%ldconfig_scriptlets + +%files +%doc README.md +%license LICENSE.txt +%{_libdir}/lib%{name}.so.* +%{_libdir}/libbpf.so.* + +%files devel +%{_libdir}/lib%{name}.so +%{_libdir}/libbpf.so +%{_libdir}/pkgconfig/lib%{name}.pc +%{_includedir}/%{name}/ + +%files -n python3-%{name} +%{python3_sitelib}/%{name}* + +%files doc +# % dir % {_docdir}/% {name} +%doc %{_datadir}/%{name}/examples/ +%if %{without lua} +%exclude %{_datadir}/%{name}/examples/lua +%endif + +%files tools +%dir %{_datadir}/%{name} +%dir %{_datadir}/%{name}/tools +%dir %{_datadir}/%{name}/introspection +%{_datadir}/%{name}/tools/* +%{_datadir}/%{name}/introspection/* +%exclude %{_datadir}/%{name}/tools/old/ +# inject relies on BPF_KPROBE_OVERRIDE which is not set on RHEL 8 +%exclude %{_datadir}/%{name}/tools/inject +# Neither btrfs nor zfs are available on RHEL8 +%exclude %{_datadir}/%{name}/tools/btrfs* +%exclude %{_datadir}/%{name}/tools/zfs* +%{_mandir}/man8/* + +%if %{with lua} +%files lua +%{_bindir}/bcc-lua +%endif + + +%changelog +* Thu Dec 13 2018 Jerome Marchand - 0.7.0-5 +- Fix biolatency -D +- Fix biotop manpage +- Rebuild for LLVM 7.0.1 (from Tom Stellard) + +* Mon Dec 10 2018 Jerome Marchand - 0.7.0-4 +- Fix bio* tools + +* Mon Nov 05 2018 Jerome Marchand - 0.7.0-3 +- Fix multiple bytes/string encoding issues +- Fix misc covscan warning + +* Mon Oct 15 2018 Tom Stellard - 0.7.0-2 +- Drop explicit dependency on clang-libs + +* Fri Oct 12 2018 Jerome Marchand - 0.7.0-1 +- Rebase on bcc-7.0.0 +- Remove useless tools (zfs*, btrfs* and inject) + +* Thu Sep 20 2018 Jerome Marchand - 0.6.1-1 +- Rebase on bcc-0.6.1 + +* Thu Sep 20 2018 Jerome Marchand - 0.6.0-6 +- llcstat: print a nicer error message on virtual machine +- Add NSS support to sslsniff +- Fixes miscellaneous error uncovered by covscan + +* Wed Aug 08 2018 Tom Stellard - 0.6.0-5 +- Use llvm-toolset-6.0 prefix for clang-libs dependency + +* Fri Aug 03 2018 Tom Stellard - 0.6.0-4 +- Rebuld for llvm-toolset-6.0 + +* Wed Jul 18 2018 Jerome Marchand - 0.6.0-3 + - Disable lua on all arches + +* Tue Jun 26 2018 Jerome Marchand - 0.6.0-2 +- Add clang-libs requirement +- Fix manpages symlinks + +* Tue Jun 19 2018 Jerome Marchand - 0.6.0-1 +- Rebase on bcc-0.6.0 + +* Thu May 24 2018 Jerome Marchand - 0.5.0-5 +- Enables build on ppc64(le) and s390x arches + +* Thu Apr 05 2018 Rafael Santos - 0.5.0-4 +- Resolves #1555627 - fix compilation error with latest llvm/clang + +* Wed Feb 07 2018 Fedora Release Engineering - 0.5.0-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_28_Mass_Rebuild + +* Fri Feb 02 2018 Igor Gnatenko - 0.5.0-2 +- Switch to %%ldconfig_scriptlets + +* Wed Jan 03 2018 Rafael Santos - 0.5.0-1 +- Rebase to new released version + +* Thu Nov 16 2017 Rafael Santos - 0.4.0-4 +- Resolves #1517408 - avoid conflict with other manpages + +* Thu Nov 02 2017 Rafael Santos - 0.4.0-3 +- Use weak deps to not require lua subpkg on ppc64(le) + +* Wed Nov 01 2017 Igor Gnatenko - 0.4.0-2 +- Rebuild for LLVM5 + +* Wed Nov 01 2017 Rafael Fonseca - 0.4.0-1 +- Resolves #1460482 - rebase to new release +- Resolves #1505506 - add support for LLVM 5.0 +- Resolves #1460482 - BPF module compilation issue +- Partially address #1479990 - location of man pages +- Enable ppc64(le) support without lua +- Soname versioning for libbpf by ignatenkobrain + +* Wed Aug 02 2017 Fedora Release Engineering - 0.3.0-4 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Binutils_Mass_Rebuild + +* Wed Jul 26 2017 Fedora Release Engineering - 0.3.0-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Mass_Rebuild + +* Thu Mar 30 2017 Igor Gnatenko - 0.3.0-2 +- Rebuild for LLVM4 +- Trivial fixes in spec + +* Fri Mar 10 2017 Rafael Fonseca - 0.3.0-1 +- Rebase to new release. + +* Fri Feb 10 2017 Fedora Release Engineering - 0.2.0-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_26_Mass_Rebuild + +* Tue Jan 10 2017 Rafael Fonseca - 0.2.0-2 +- Fix typo + +* Tue Nov 29 2016 Rafael Fonseca - 0.2.0-1 +- Initial import