ports system woes

soralx at cydem.org soralx at cydem.org
Wed Mar 26 02:01:42 PDT 2008


Folks,

 are there any plans to rewrite the ports/packages system? Maybe someone
 started work on improving things in this area already?

 The thought that pkg_* tools and Mk/* scripts might be somewhat
 inefficient had crossed my mind before, when at last modular Xorg
 exposed all the inefficiencies in these tools. Basically, pkg_* and
 portupgrade seem to use very inefficient algorithms all over the place;
 they've become nearly useless on my system these days.

 Consider pkg_delete, for example:
  `time pkg_delete /var/db/pkg/rxvt-unicode-9.02/`
    real    7m4.207s
    user    0m4.083s
    sys     0m16.348s

 This one was rather benign. Many of bigger packages take 15 minutes to
 get removed. Now, all the test were conducted on Pentium 4-M 1.2GHz
 notebook with 256M RAM and MHT2040AH hard drive (5400RPM, 8M buffer).
 Notice the amount of RAM. Also:

[root at freen0de /var/db/pkg]# ll|wc -l
     959

 For the rxvt case, the offending function is matchbyorigin() inside
 src/usr.sbin/pkg_install/lib/match.c, called from delete/perform.c
 function pkg_do(). Here's a snippet from pkg_do():

    for (p = Plist.head; p ; p = p->next) {
        if (p->type != PLIST_PKGDEP)
            continue;
        deporigin = \
(p->next != NULL && p->next->type == PLIST_DEPORIGIN) ?p->next->name :
                                                         NULL;
        if (Verbose) {
            printf("Trying to remove dependency on package '%s'", p->name);
            if (deporigin != NULL)
                printf(" with '%s' origin", deporigin);
            printf(".\n");
        }
        if (!Fake) {
depnames = (deporigin != NULL) ? matchbyorigin(deporigin, NULL) :
                                             NULL;
            if (depnames == NULL) {
                depnames = alloca(sizeof(*depnames) * 2);
                depnames[0] = p->name;
                depnames[1] = NULL;
            }
            for (i = 0; depnames[i] != NULL; i++)
                undepend(depnames[i], pkg);
        }
    }

 What exactly "removing dependency on package" means? (i.e., what is
 undepend() for?) I didn't figure it out yet (anyone?), but from what I
 read from the code, it calls matchbyorigin(deporigin, NULL) -- which
 is the super-slow part -- while processing each @pkgdep one-by-one.
 matchbyorigin(), in turn, scans +CONTENTS of each entry in db/pkg/* to get
 '@comment ORIGIN:' value. As a result, each operation (like "Trying to
 remove dependency on package 'xineramaproto-1.1.2' with 'x11/xineramaproto'
 origin.") takes 3-4 seconds (first one ~30s), and there are ~120(!)
 dependencies for urxvt (I imagine them evil penguins are all too glad to
 make everything complete chaos, but hopefully making things _so_ modular
 has at least some benefits).

 Replacing 'if (!Fake)' with 'if (FALSE)' makes pkg_delete go _really_ fast
 -- almost instantaneous. I didn't notice any side effects of that hack so
 far, but there sure are some. Anyway, I understand the desire to move
 functions like matchbyorigin() to libinstall, but can't that one at least
 be made to accept an array of strings (package names) and process them in
 a single pass? The whole of src/usr.sbin/pkg_install needs rewriting, IMO,
 as this is just one of many examples of slow code.

 Here's another example. Checking if a package is already installed was
 also quite slow. Much of the slowness, turns out, was caused by the
 following in ports/Mk/bsd.openssl.mk:

.if     !defined(OPENSSL_PORT) && \
        exists(${LOCALBASE}/lib/libcrypto.so)
# find installed port and use it for dependency
PKG_DBDIR?=             ${DESTDIR}/var/db/pkg
OPENSSL_INSTALLED!=     grep -l -r "^lib/libssl.so." "${PKG_DBDIR}" | \
                        while read contents; do \
                                sslprefix=`grep "^@cwd " "$${contents}" | ${HEAD} -n 1`; \
                                if test "$${sslprefix}" = "@cwd ${LOCALBASE}" ; then \
                                        echo "$${contents}"; break; fi; done
OPENSSL_PORT!=          grep "^@comment ORIGIN:" "${OPENSSL_INSTALLED}" | ${CUT} -d : -f 2
OPENSSL_SHLIBFILE!=     grep "^lib/libssl.so." "${OPENSSL_INSTALLED}"
OPENSSL_SHLIBVER?=      ${OPENSSL_SHLIBFILE:E}
.endif
OPENSSL_PORT?=          security/openssl

 Simply defining 'OPENSSL_PORT=security/openssl' in make.conf helped a lot
 (although really, we need to figure a better method of finding installed
 openssl port -- perhaps a script that updates OPENSSL_PORT in make.conf after
 port installs?). It is still fairly slow, though.

 Another example:

[root at freen0de /usr/ports/x11/nvidia-driver-96xx]# make fetch
===>  Vulnerability check disabled, database not found
===>  Found saved configuration for nvidia-driver-96.43.01
=> NVIDIA-FreeBSD-x86-96.43.05.tar.gz doesn't seem to exist in /usr/ports/distfiles/.
=> Attempting to fetch from http://jp.download.nvidia.com/freebsd/96.43.05/.
NVIDIA-FreeBSD-x86-96.43.05.tar.gz            100% of 9444 kB   71 kBps
[root at freen0de /usr/ports/x11/nvidia-driver-96xx]# time make fetch
===>  Vulnerability check disabled, database not found
===>  Found saved configuration for nvidia-driver-96.43.01

real    0m53.340s
user    0m0.299s
sys     0m0.886s
[root at freen0de /usr/ports/x11/nvidia-driver-96xx]# time make maintainer
<deleted>

real    0m22.817s
user    0m0.265s
sys     0m0.685s
[root at freen0de /usr/ports/x11/nvidia-driver-96xx]#

 So all these examples are only tip of the iceberg that is currently making
 pkg_* & ports sink, but of course no other bugs are as serious as the one with
 pkg_delete (or so I hope).

 And I'm not even talking about portupgrade. Dragging it's feet at a snail pace,
 running sluggish 'pkgdb -Q' each tiny step... it's a rather useful tool. But,
 I believe portupgrade is not really needed -- if FreeBSD's base ports/packaging
 tools get fixed and extended, they'd do the job (yah, there's portmaster, but
 with broken pkg_*, it's just as good as portupgrade).

 BTW, to make `portupgrade -a` at all useful, a while ago I whipped up a bash
 command that lists all dependencies of installed packages:

export PORTSBASE=/usr/ports; cd /var/db/pkg
for dir in `ls`; do tmp=`(grep '@comment ORIGIN' "${dir}/+CONTENTS"|awk -F ":" '{ print $2 }')`; echo ${tmp}; cd ${PORTSBASE}/${tmp} && (make package-depends-list|awk -F " " '{ print $3 }'); cd /var/db/pkg/; done|sort|uniq > /tmp/deps.lst

 Allow it 3-5 hours per 1000 installed ports 8-) The good thing is that
 theoretically this should get all the dependencies in fresh ports tree
 right, when fed from old (not updated) /var/db/pkg, so then you could do:

for dir in `cat /tmp/deps.lst`; do cd ${PORTSBASE}/${dir} && make config-conditional; done

 which in ~10 minutes should take care care of all the new unconfigured
 (`make config`) ports in single attempt. Practically, there are always some
 problems with the gazillion versions of tcl* and tk* (go to ports tree and
 manually run `make config` for each and every version of tcl* and tk* that's
 there), and any cases where 'dialog' coredumps (long lists of options).
 /tmp/deps.lst can be usually reused. Now, just to convert these commands to
 normal /bin/sh script...

[SorAlx]  ridin' VN1500-B2


More information about the freebsd-hackers mailing list