hg.rust: add BuildInfo symbols

Summary:
BuildInfo symbols are generated for fbcode binaries, and scanned by the
internal procprint tool. The procprint tool also scans hg binaries, but cannot
extract any build information.

The procprint data is used by Push4Push to find out job owners running ancient
software.

This diff inserts BuildInfo symbols so Push4Push (and we) can use them to
figure out people running old versions of hg and send tasks to them.

Note rpmbuild strips symbols by default and is hard to customize. For now, I
just disable the debuginfo packages to preserve the symbols. If space usage or
runtime performance becomes an issue, we can try other workarounds (ex. provide
a shim of `%_find_debuginfo_dwz_opts` that skips `hg.rust`, or provide a shim
of `%__strip`, etc).

By default, the symbols only get added when `setup.py` is run by `rpmbuild`.
So `make local` won't rebuild `hg.rust` every time.

Reviewed By: singhsrb

Differential Revision: D14464056

fbshipit-source-id: ac6f9dedf28cee5fc8d65e144e3d7d1c544d1eda
This commit is contained in:
Jun Wu 2019-03-20 23:40:19 -07:00 committed by Facebook Github Bot
parent b40af5890c
commit 41ccbe9f8d
5 changed files with 126 additions and 5 deletions

View File

@ -14,6 +14,7 @@ path = "../../lib/encoding"
path = "../../lib/hgpython"
[features]
buildinfo = []
default = []
with_chg = []
hgdev = ["hgpython/hgdev"]

View File

@ -14,6 +14,11 @@ fn main() {
"cargo:rerun-if-changed={}",
lib_dir.join("libchg.a").display()
);
#[cfg(feature = "buildinfo")]
println!(
"cargo:rerun-if-changed={}",
lib_dir.join("buildinfo.a").display()
);
}
}
}

View File

@ -0,0 +1,7 @@
// Copyright Facebook, Inc. 2019
#[cfg(feature = "buildinfo")]
#[link(name = "buildinfo", kind = "static")]
extern "C" {
pub fn print_buildinfo();
}

View File

@ -1,4 +1,5 @@
// Copyright Facebook, Inc. 2018
#[cfg(feature = "with_chg")]
extern crate dirs;
extern crate encoding;
@ -7,6 +8,7 @@ extern crate hgpython;
extern crate libc;
use hgpython::HgPython;
mod buildinfo;
#[cfg(feature = "with_chg")]
mod chg;
#[cfg(feature = "with_chg")]
@ -23,6 +25,20 @@ fn call_embedded_python() {
}
fn main() {
#[cfg(feature = "buildinfo")]
{
// This code path keeps buildinfo-related symbols alive.
use std::env;
if let Some(arg0) = env::args().nth(0) {
if arg0.ends_with("buildinfo") {
unsafe {
buildinfo::print_buildinfo();
}
return;
}
}
}
#[cfg(feature = "with_chg")]
maybe_call_chg();
call_embedded_python();

102
setup.py
View File

@ -18,6 +18,7 @@ import os
import py_compile
import re
import shutil
import socket
import stat
import struct
import subprocess
@ -452,15 +453,24 @@ def localhgenv():
return env
def pickversion():
hg = findhg()
hg = findhg()
def hgtemplate(template, cast=None):
if not hg:
# if hg is not found, fallback to a fixed version
return "4.4.2"
return None
result = sysstr(hg.run(["log", "-r.", "-T", template]))
if result and cast:
result = cast(result)
return result
def pickversion():
# New version system: YYMMDD_HHmmSS_hash
# This is duplicated a bit from build_rpm.py:auto_release_str()
template = '{sub("([:+-]|\d\d\d\d$)", "",date|isodatesec)} {node|short}'
out = sysstr(hg.run(["log", "-r.", "-T", template]))
# if hg is not found, fallback to a fixed version
out = hgtemplate(template) or ""
# Some tools parse this number to figure out if they support this version of
# Mercurial, so prepend with 4.4.2.
# ex. 4.4.2_20180105_214829_58fda95a0202
@ -502,6 +512,77 @@ if not os.path.isdir(builddir):
ensureexists(builddir)
def writebuildinfoc():
"""Write build/buildinfo.c"""
commithash = hgtemplate("{node}")
commitunixtime = hgtemplate('{sub("[^0-9].*","",date)}', cast=int)
# Search 'extractBuildInfoFromELF' in fbcode for supported fields.
buildinfo = {
"Host": socket.gethostname(),
"PackageName": os.environ.get("RPM_PACKAGE_NAME")
or os.environ.get("PACKAGE_NAME"),
"PackageRelease": os.environ.get("RPM_PACKAGE_RELEASE")
or os.environ.get("PACKAGE_RELEASE"),
"PackageVersion": os.environ.get("RPM_PACKAGE_VERSION")
or os.environ.get("PACKAGE_VERSION"),
"Path": os.getcwd(),
"Platform": os.environ.get("RPM_OS"),
"Revision": commithash,
"RevisionCommitTimeUnix": commitunixtime,
"TimeUnix": int(time.time()),
"UpstreamRevision": commithash,
"UpstreamRevisionCommitTimeUnix": commitunixtime,
"User": os.environ.get("USER"),
}
buildinfosrc = """
#include <stdio.h>
#include <time.h>
"""
for name, value in sorted(buildinfo.items()):
if isinstance(value, str):
buildinfosrc += 'const char *BuildInfo_k%s = "%s";\n' % (
name,
value.replace('"', '\\"'),
)
elif isinstance(value, int):
# The only usage of int is timestamp
buildinfosrc += "const time_t BuildInfo_k%s = %d;\n" % (name, value)
buildinfosrc += """
/* This function keeps references of the symbols and prevents them from being
* optimized out if this function is used. */
void print_buildinfo() {
"""
for name, value in sorted(buildinfo.items()):
if isinstance(value, str):
buildinfosrc += (
' fprintf(stderr, "%(name)s: %%s (at %%p)\\n", BuildInfo_k%(name)s, BuildInfo_k%(name)s);\n'
% {"name": name}
)
elif isinstance(value, int):
buildinfosrc += (
' fprintf(stderr, "%(name)s: %%lu (at %%p)\\n", (long unsigned)BuildInfo_k%(name)s, &BuildInfo_k%(name)s) ;\n'
% {"name": name}
)
buildinfosrc += """
}
"""
path = pjoin(builddir, "buildinfo.c")
write_if_changed(path, buildinfosrc)
return path
# If NEED_BUILDINFO is set, write buildinfo.
# For rpmbuild, imply NEED_BUILDINFO.
needbuildinfo = bool(os.environ.get("NEED_BUILDINFO", "RPM_PACKAGE_NAME" in os.environ))
if needbuildinfo:
buildinfocpath = writebuildinfoc()
try:
oldpolicy = os.environ.get("HGMODULEPOLICY", None)
os.environ["HGMODULEPOLICY"] = "py"
@ -1833,6 +1914,16 @@ libraries = [
},
),
]
if needbuildinfo:
libraries += [
(
"buildinfo",
{
"sources": [buildinfocpath],
"extra_args": filter(None, cflags + [WALL, PIC]),
},
)
]
if not iswindows:
libraries.append(
@ -2104,6 +2195,7 @@ hgmainfeatures = (
filter(
None,
[
"buildinfo" if needbuildinfo else None,
"hgdev" if os.environ.get("HGDEV") else None,
"with_chg" if not iswindows else None,
],