From patchwork Tue Jul 23 22:35:37 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Omar Sandoval X-Patchwork-Id: 94389 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 52E0E3857C4F for ; Tue, 23 Jul 2024 22:36:08 +0000 (GMT) X-Original-To: elfutils-devel@sourceware.org Delivered-To: elfutils-devel@sourceware.org Received: from mail-oa1-x2f.google.com (mail-oa1-x2f.google.com [IPv6:2001:4860:4864:20::2f]) by sourceware.org (Postfix) with ESMTPS id 7292F3858D34 for ; Tue, 23 Jul 2024 22:35:55 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 7292F3858D34 Authentication-Results: sourceware.org; dmarc=none (p=none dis=none) header.from=osandov.com Authentication-Results: sourceware.org; spf=none smtp.mailfrom=osandov.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 7292F3858D34 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=2001:4860:4864:20::2f ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1721774157; cv=none; b=cC2ZX92jzwYaVdzt3Iolfi0F3JukQV/ZBLWqlB6jIgJtNcrfBBAkDhKJeG8A4YMMGwHWWdzBtq+/FY1RmVz+RmORQqvswR7gezNMbZtQOXLeYq7O5W2rp0s7yoIqe6nq1j1JjABUEWrF3p8iofK2pe1fLX8417pFl3zbmCoVBzc= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1721774157; c=relaxed/simple; bh=8vQiclWFbsx+3K/weG4sVm5OTnKn5KkkGLuIdqwnPHQ=; h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version; b=HcpbsLcERxX4fbYz/GPcUbWFTGlnnSLZcCgmAaK37cfwZ1j0xaADKh1887EMft2ELqFxsou15hr98BWaYRPap4zVcJCLdE3ThQu8MoXJpeshdafbb47faWAaP/X763wVaac6NYHPLPYBjV090UM1arDgec1lM8dQzZ5Z3UNBZNg= ARC-Authentication-Results: i=1; server2.sourceware.org Received: by mail-oa1-x2f.google.com with SMTP id 586e51a60fabf-26109c97728so2988001fac.0 for ; Tue, 23 Jul 2024 15:35:55 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=osandov-com.20230601.gappssmtp.com; s=20230601; t=1721774154; x=1722378954; darn=sourceware.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=jVyuRH8a24N2shL0mW04I1Et6TNCY2O7iCoC94+iBck=; b=MVM7kmXWb6v7SXc05hlxUnk8YfccoXWehXY9ll1mF+H70TIWMOfEGD6w0Cx2z971Er WFF1doMnJuI5rKGkUUXoJWnQsWxnvHESgqDX4rKCizxCVahZhTpgoUy2GfAOjKHL9vfk b7qCIjcZAL6B4TQDfD4VCZkUkQnmH6qs05S07xSh08Xe4HXsSVdhA61bEGgxQDxjzSPT yCDctDt0Yf7O2DefMXhXNaNXZ0Gyw1H+HNeFE84/kALcrhTXf1xHTnUyT6juVMgzVNGg L2GMX5S/5Nd5R0HegQ7Mo3iT77Uho+f8htMuwlifwFK/PdShHbwYqAQfdN+2+8m7hi+f /+tQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1721774154; x=1722378954; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=jVyuRH8a24N2shL0mW04I1Et6TNCY2O7iCoC94+iBck=; b=RKEJvHAb19iVNcpLYsz3f4BtdOJhyWpota6Lrb0EqXcr0WJsLqY74TtspkN3HjMG8a LsvkyHQTHw2Ub0IGykeiKt7cbLSwd9RHIzWbF0vnDAyfPAauQDB/MJcOk7th34eKjFUW s9+xELd3Q68jWhaMffVVgRZQX9rTTZoHrMxrUWaU8EUBLAZLao+zZFs7bEkW7QUI7Rxe SMITo3HJ7iqChZRoi+DvfNiez2RKSC8V8KisBr/5GSmMnvss4miVnGuFWIag/m3GOMGS U3i+s62UA56htDzN44seSp5mhgZFXweGexZRuB8pLn1K/PZUotoQNguwVMFFRJfRirn0 rdlw== X-Gm-Message-State: AOJu0YxqLX84EpIR+ah0INi3HQf4edjcQe8iwznhevwsG4CZEfh+Eefr 25TWNV4xtuRijD8Dxigaa6yEE1xu/AsEgiLjEJdBjIlYauImFOPMpX8ahxW2C1B7fGhydD99TmG M X-Google-Smtp-Source: AGHT+IHI/PlFqCBNrfc5XwX4XVa/OSJLFGcB7L0j1RRZlqU/KPdIXnPT6UtM+UUai5Y97DTLBo10fQ== X-Received: by 2002:a05:6870:71cd:b0:261:f8e:a37a with SMTP id 586e51a60fabf-264876830abmr1371800fac.14.1721774154470; Tue, 23 Jul 2024 15:35:54 -0700 (PDT) Received: from telecaster.thefacebook.com ([2620:10d:c090:500::5:b6d3]) by smtp.gmail.com with ESMTPSA id d2e1a72fcca58-70d241803fdsm4308079b3a.220.2024.07.23.15.35.53 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 23 Jul 2024 15:35:53 -0700 (PDT) From: Omar Sandoval To: elfutils-devel@sourceware.org Cc: "Frank Ch . Eigler" , Aaron Merey , linux-debuggers@vger.kernel.org Subject: [PATCH v5 1/7] debuginfod: fix skipping source file Date: Tue, 23 Jul 2024 15:35:37 -0700 Message-ID: <1b3f0374f89af4c6bbba9fa1b0f15034864c0afc.1721773977.git.osandov@fb.com> X-Mailer: git-send-email 2.45.2 In-Reply-To: References: MIME-Version: 1.0 X-Spam-Status: No, score=-11.5 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, GIT_PATCH_0, RCVD_IN_DNSWL_NONE, SPF_HELO_NONE, SPF_NONE, TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: elfutils-devel@sourceware.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Elfutils-devel mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: elfutils-devel-bounces~patchwork=sourceware.org@sourceware.org From: Omar Sandoval dwarf_extract_source_paths explicitly skips source files that equal "", but dwarf_filesrc may return a path like "dir/". Check for and skip that case, too. In particular, the test debuginfod RPMs have paths like this. However, the test cases didn't catch this because they have a bug, too: they follow symlinks, which results in double-counting every file. Fix that, too. Signed-off-by: Omar Sandoval --- debuginfod/debuginfod.cxx | 3 ++- tests/run-debuginfod-archive-groom.sh | 2 +- tests/run-debuginfod-extraction.sh | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/debuginfod/debuginfod.cxx b/debuginfod/debuginfod.cxx index 305edde8..92022f3d 100644 --- a/debuginfod/debuginfod.cxx +++ b/debuginfod/debuginfod.cxx @@ -3446,7 +3446,8 @@ dwarf_extract_source_paths (Elf *elf, set& debug_sourcefiles) if (hat == NULL) continue; - if (string(hat) == "") // gcc intrinsics, don't bother record + if (string(hat) == "" + || string_endswith(hat, "")) // gcc intrinsics, don't bother record continue; string waldo; diff --git a/tests/run-debuginfod-archive-groom.sh b/tests/run-debuginfod-archive-groom.sh index e2c394ef..0131158f 100755 --- a/tests/run-debuginfod-archive-groom.sh +++ b/tests/run-debuginfod-archive-groom.sh @@ -109,7 +109,7 @@ for i in $newrpms; do rpm2cpio ../$i | cpio -ivd; cd ..; done -sourcefiles=$(find -name \*\\.debug \ +sourcefiles=$(find -name \*\\.debug -type f \ | env LD_LIBRARY_PATH=$ldpath xargs \ ${abs_top_builddir}/src/readelf --debug-dump=decodedline \ | grep mtime: | wc --lines) diff --git a/tests/run-debuginfod-extraction.sh b/tests/run-debuginfod-extraction.sh index da6b25cf..f49dc6f6 100755 --- a/tests/run-debuginfod-extraction.sh +++ b/tests/run-debuginfod-extraction.sh @@ -94,7 +94,7 @@ for i in $newrpms; do rpm2cpio ../$i | cpio -ivd; cd ..; done -sourcefiles=$(find -name \*\\.debug \ +sourcefiles=$(find -name \*\\.debug -type f \ | env LD_LIBRARY_PATH=$ldpath xargs \ ${abs_top_builddir}/src/readelf --debug-dump=decodedline \ | grep mtime: | wc --lines) From patchwork Tue Jul 23 22:35:38 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Omar Sandoval X-Patchwork-Id: 94390 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id E55533857C4F for ; Tue, 23 Jul 2024 22:36:21 +0000 (GMT) X-Original-To: elfutils-devel@sourceware.org Delivered-To: elfutils-devel@sourceware.org Received: from mail-oi1-x22d.google.com (mail-oi1-x22d.google.com [IPv6:2607:f8b0:4864:20::22d]) by sourceware.org (Postfix) with ESMTPS id AB81A3858CD9 for ; Tue, 23 Jul 2024 22:35:56 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org AB81A3858CD9 Authentication-Results: sourceware.org; dmarc=none (p=none dis=none) header.from=osandov.com Authentication-Results: sourceware.org; spf=none smtp.mailfrom=osandov.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org AB81A3858CD9 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=2607:f8b0:4864:20::22d ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1721774158; cv=none; b=YJPnb9uJSEGFrD3zWA19uFNvbD9rZqCQNDTkM0fHA0QzFqMhVg/pGcWeiyfS4r462sM/uEsRGAYPtjvjnME6KbsAsRYZdLwbbgWVpN3BJSHw8NgGqNRwccn1rsCCBoC7cRjsQUkNmwAQznwTYmwY7TJjJJRhHIGmd3O2l1lUGhg= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1721774158; c=relaxed/simple; bh=UesB4qXCNMCR6NkFYSV2Ruj7Krbns+hMD3/e7+wTjGg=; h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version; b=a0jGO/yYyzgqP/6tI3JuIRpima+cWNZgVZdACQHfPieyLaeeVy7tOZIc8rGeilPfIyi7y1KBZGOJQ7xk8onISS7pEZIGYTLFD+5edcSvz+sceBM7EAWHWhhEiAYQINUaAfofi96LWSw2MROV66Q7rrOxsLjH51AeTu0S5zU8u+U= ARC-Authentication-Results: i=1; server2.sourceware.org Received: by mail-oi1-x22d.google.com with SMTP id 5614622812f47-3d96365dc34so3660584b6e.2 for ; Tue, 23 Jul 2024 15:35:56 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=osandov-com.20230601.gappssmtp.com; s=20230601; t=1721774155; x=1722378955; darn=sourceware.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=biAXgVGe01xXLaCXVejULNfAEwYuUbE6WaXaRROeriU=; b=Lj3q8Fgb9nEioQDrmNYSLaQWNyq1kAvaKH1pWWysHq9IWIbAjgwjxkFptiijT81OcF ER9A/eIOKLM2BkP5dfkqpupnItgEpHhXe0nhKEI+EKisMxn4d2Bim7N6HCKBEXLk08MM oh7elS+VUpJxWcdWF6eIjtFfooLLxzVlhfnmJI4URX5IHRG/YlQED3sRrr4uVi3M1d2Z klsXVMnpz35aDVho52pUeGVc+Y5/udRjzA7VyjJYRmWcQpVKBCJVvgpekmHr5C+dxuOO wfJAeP0oioib+uaEtjQH14xtld2cux6vRnFq9Tod1HueQB9qxlk2k3dovQvIKPdwk4Iu aTcg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1721774155; x=1722378955; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=biAXgVGe01xXLaCXVejULNfAEwYuUbE6WaXaRROeriU=; b=LpA5h/1UNt4XHLHV59ELnOdObuDo/7y+G3+OwjjXDbEN2oU3kIVVjoGSjATBzcaTDQ cy8B1rySMHy5qCn1BzRxzclGU4yibn8zF8i3dOru6CHGVLwE+ltsEbg7bRqRxpL1TNow yUh3IGZQAxA0ozLm8Q9UchqXYPFJI6u9jZW8C2BnpG1WrIcSIco4KtET0rK73/dC1tdh FOKWAacq5TKJX/xYuTN+yk9VrwVWmdtESVZ8NkPmGUvbzNJ/DcC7slsr7Ig/RiW0gLd0 1DMx2jv4KducI8xjxDLEufLR8GiK8ypGIG5qZ/rHtzipLYMjphkMTBIQQTCTej9hPcZw yGYA== X-Gm-Message-State: AOJu0YwQiZBnQe2ACpFDjMeXJdvoaZY/9psGQMIO+2wR4RYiNsiWX0RQ rLsQRRn+IigTY1h9rSe5250sTPgLck3PRAOHTqZa1oo41uWWHwp2sloDtb55LR2zzgbUK189b00 w X-Google-Smtp-Source: AGHT+IFpKUhoIS7QW5vxkxCVm/ZnxxuwBPIA41UuPdN0j8VE/gvWlVxTEKffWgA6zC1P56pCFj4WLg== X-Received: by 2002:a05:6870:b41f:b0:261:6ce:b805 with SMTP id 586e51a60fabf-263ab679b23mr10666989fac.44.1721774155698; Tue, 23 Jul 2024 15:35:55 -0700 (PDT) Received: from telecaster.thefacebook.com ([2620:10d:c090:500::5:b6d3]) by smtp.gmail.com with ESMTPSA id d2e1a72fcca58-70d241803fdsm4308079b3a.220.2024.07.23.15.35.54 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 23 Jul 2024 15:35:55 -0700 (PDT) From: Omar Sandoval To: elfutils-devel@sourceware.org Cc: "Frank Ch . Eigler" , Aaron Merey , linux-debuggers@vger.kernel.org Subject: [PATCH v5 2/7] tests/run-debuginfod-fd-prefetch-caches.sh: disable fdcache limit check Date: Tue, 23 Jul 2024 15:35:38 -0700 Message-ID: <7095c9beb10b5495ddc08851e678995b8825217e.1721773977.git.osandov@fb.com> X-Mailer: git-send-email 2.45.2 In-Reply-To: References: MIME-Version: 1.0 X-Spam-Status: No, score=-11.7 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, GIT_PATCH_0, RCVD_IN_DNSWL_NONE, SPF_HELO_NONE, SPF_NONE, TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: elfutils-devel@sourceware.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Elfutils-devel mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: elfutils-devel-bounces~patchwork=sourceware.org@sourceware.org From: Omar Sandoval Since commit acd9525e93d7 ("PR31265 - rework debuginfod archive-extract fdcache"), the fdcache limit is only applied when a new file is interned and it has been at least 10 seconds since the limit was last applied. This means that the fdcache can go over the limit temporarily. run-debuginfod-fd-prefetch-caches.sh happens to avoid tripping over this because of lucky sizes of the files used in the test. However, adding new files for an upcoming test exposed this failure. Disable this part of the test for now. Signed-off-by: Omar Sandoval --- tests/run-debuginfod-fd-prefetch-caches.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/run-debuginfod-fd-prefetch-caches.sh b/tests/run-debuginfod-fd-prefetch-caches.sh index 3db78ade..90730555 100755 --- a/tests/run-debuginfod-fd-prefetch-caches.sh +++ b/tests/run-debuginfod-fd-prefetch-caches.sh @@ -99,6 +99,9 @@ kill $PID1 wait $PID1 PID1=0 +# Since we now only limit the fd cache every 10 seconds, it can temporarily go +# over the limit. That makes this part of the test unreliable. +if false; then ######### # Test mb limit on fd cache ######### @@ -148,3 +151,4 @@ kill $PID1 wait $PID1 PID1=0 exit 0 +fi From patchwork Tue Jul 23 22:35:39 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Omar Sandoval X-Patchwork-Id: 94392 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 064543858417 for ; Tue, 23 Jul 2024 22:36:38 +0000 (GMT) X-Original-To: elfutils-devel@sourceware.org Delivered-To: elfutils-devel@sourceware.org Received: from mail-pf1-x431.google.com (mail-pf1-x431.google.com [IPv6:2607:f8b0:4864:20::431]) by sourceware.org (Postfix) with ESMTPS id 8FEEC3858433 for ; Tue, 23 Jul 2024 22:35:58 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 8FEEC3858433 Authentication-Results: sourceware.org; dmarc=none (p=none dis=none) header.from=osandov.com Authentication-Results: sourceware.org; spf=none smtp.mailfrom=osandov.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 8FEEC3858433 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=2607:f8b0:4864:20::431 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1721774161; cv=none; b=aq3a0UY4y2+zxfsiHGHcSv8M6jR0s6mLOdSBiTh98Rwf2vv6yKFda4rb+fE4g2Tn4DLXTq9gEytAi6UiBt2mvuWjVMubM1AL4ir601mpECKLrf4NCKdpusa2bkeeW7TkH0AVVOA+K6COuB5EGrH+ZEJ+FqIz5ZPVpXblgtsOEKI= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1721774161; c=relaxed/simple; bh=7po6iuMq0yOzagehOqCkbkdF4/fDRh9HsclVqFQrYas=; h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version; b=BqSTPikgw6dW6yiYo0fCMQNwxIyYcq8pZQCVq44cN0yek2PT0CwFtvrL0alHtxciS8Reh6Pem2RSn2bbz93bFa3DTMlePEjVl0f8zMc9AADSMFc4ejMKrem5M+T6fpZPGtUlhNF+laS4zcxxI3V4I9KN4TAlmLBKzWiAq5OgxDA= ARC-Authentication-Results: i=1; server2.sourceware.org Received: by mail-pf1-x431.google.com with SMTP id d2e1a72fcca58-70d1cbbeeaeso1766726b3a.0 for ; Tue, 23 Jul 2024 15:35:58 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=osandov-com.20230601.gappssmtp.com; s=20230601; t=1721774157; x=1722378957; darn=sourceware.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=4paaUY15mWOyU0kjCYqRC9axHcB+L/opfNmc+xF9ups=; b=alWSnH2bnzL0H52AwY0nY+/DKp35eK+t+q+GthANvBIa8pQgDMCWj4Q8aMzg1yDM4F ON6VUieht1Fn6i7wbelavlCCaHq5SdBDoUzu3kzpDpC0rOw9lcVo55fSqICzvBsecKfk Mo1xzKlOfwDR+Hj+Vl42EPTdKs86+9qhI9CQTadocvY11Jg2s5FL4/f31OahUYBQIrh6 jGgP1w7lcXhda3lpyVwjTToEMazXeB+ObECBybxr3UEligdSJSM4R7LOgSddSt5bBKw2 WLW1QpisDDJKsP+lWJy0VihqIH2D84UcsjHQGBToShno35SXHTVgviQHkVN7Z9fyMSXl 6R5w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1721774157; x=1722378957; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=4paaUY15mWOyU0kjCYqRC9axHcB+L/opfNmc+xF9ups=; b=TKJoRDZyhSWgmPQ3JeCH6Uh2HntDu2NZuf+KGMv4yOJ0/wX94WR5oIkzeiEB7Guyjv dDreoauieu0FkycBW/n8rKL49V7zT1y3D55B1Kn7wEatB/yYn/dejNrZ7otK1F66vmqR L+kNyIQQfbRsBe1Ja+z7xdlU1fGRxe+mNAtn7mVssgGcH5Ak5QAqDSGzQrcVmY027l/8 67GZe2XFiks2V06qsZuH18/7jCBmVe5bl8ewFr7JL9OeUljm2ld8Lm/IYzDnM8QLVZGz uHWyuFajI1Bu3u5WKXdoExTJiT0YxGmIeptsMZWquA+1Ck8t5xJYUhIXyw/4VvfYLRoP pgSA== X-Gm-Message-State: AOJu0YyyG5NBHhoIuc0TxO5UWEk69fc3j1xzHn7olnwcH7Z05cKtuLBc GNAaUTX2AEtoMpGUtYsiLjvCW/DxJKyJjqTmeglpR5MzI/bQGeZ0bbxSeWsuPTLgmh6c/lAMaCf A X-Google-Smtp-Source: AGHT+IHoklk6xLktE+wkKzHVFPE9dC7QZnMbV3xke+zpxwrLT9RZkfCyfMb2FaMXwEX2PhPQnw5Tbw== X-Received: by 2002:a05:6a00:238d:b0:70d:2ba1:2402 with SMTP id d2e1a72fcca58-70e9971b63bmr1469346b3a.29.1721774156917; Tue, 23 Jul 2024 15:35:56 -0700 (PDT) Received: from telecaster.thefacebook.com ([2620:10d:c090:500::5:b6d3]) by smtp.gmail.com with ESMTPSA id d2e1a72fcca58-70d241803fdsm4308079b3a.220.2024.07.23.15.35.55 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 23 Jul 2024 15:35:56 -0700 (PDT) From: Omar Sandoval To: elfutils-devel@sourceware.org Cc: "Frank Ch . Eigler" , Aaron Merey , linux-debuggers@vger.kernel.org Subject: [PATCH v5 3/7] debuginfod: factor out common code for responding from an archive Date: Tue, 23 Jul 2024 15:35:39 -0700 Message-ID: X-Mailer: git-send-email 2.45.2 In-Reply-To: References: MIME-Version: 1.0 X-Spam-Status: No, score=-11.9 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, GIT_PATCH_0, RCVD_IN_DNSWL_NONE, SPF_HELO_NONE, SPF_NONE, TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: elfutils-devel@sourceware.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Elfutils-devel mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: elfutils-devel-bounces~patchwork=sourceware.org@sourceware.org From: Omar Sandoval handle_buildid_r_match has two very similar branches where it optionally extracts a section and then creates a microhttpd response. In preparation for adding a third one, factor it out into a function. Signed-off-by: Omar Sandoval --- debuginfod/debuginfod.cxx | 213 +++++++++++++++++--------------------- 1 file changed, 96 insertions(+), 117 deletions(-) diff --git a/debuginfod/debuginfod.cxx b/debuginfod/debuginfod.cxx index 92022f3d..24702c23 100644 --- a/debuginfod/debuginfod.cxx +++ b/debuginfod/debuginfod.cxx @@ -1965,6 +1965,81 @@ string canonicalized_archive_entry_pathname(struct archive_entry *e) } +// NB: takes ownership of, and may reassign, fd. +static struct MHD_Response* +create_buildid_r_response (int64_t b_mtime0, + const string& b_source0, + const string& b_source1, + const string& section, + const string& ima_sig, + const char* tmppath, + int& fd, + off_t size, + time_t mtime, + const string& metric, + const struct timespec& extract_begin) +{ + if (tmppath != NULL) + { + struct timespec extract_end; + clock_gettime (CLOCK_MONOTONIC, &extract_end); + double extract_time = (extract_end.tv_sec - extract_begin.tv_sec) + + (extract_end.tv_nsec - extract_begin.tv_nsec)/1.e9; + fdcache.intern(b_source0, b_source1, tmppath, size, true, extract_time); + } + + if (!section.empty ()) + { + int scn_fd = extract_section (fd, b_mtime0, + b_source0 + ":" + b_source1, + section, extract_begin); + close (fd); + if (scn_fd >= 0) + fd = scn_fd; + else + { + if (verbose) + obatched (clog) << "cannot find section " << section + << " for archive " << b_source0 + << " file " << b_source1 << endl; + return 0; + } + + struct stat fs; + if (fstat (fd, &fs) < 0) + { + close (fd); + throw libc_exception (errno, + string ("fstat ") + b_source0 + string (" ") + section); + } + size = fs.st_size; + } + + struct MHD_Response* r = MHD_create_response_from_fd (size, fd); + if (r == 0) + { + if (verbose) + obatched(clog) << "cannot create fd-response for " << b_source0 << endl; + close(fd); + } + else + { + inc_metric ("http_responses_total","result",metric); + add_mhd_response_header (r, "Content-Type", "application/octet-stream"); + add_mhd_response_header (r, "X-DEBUGINFOD-SIZE", to_string(size).c_str()); + add_mhd_response_header (r, "X-DEBUGINFOD-ARCHIVE", b_source0.c_str()); + add_mhd_response_header (r, "X-DEBUGINFOD-FILE", b_source1.c_str()); + if(!ima_sig.empty()) add_mhd_response_header(r, "X-DEBUGINFOD-IMASIGNATURE", ima_sig.c_str()); + add_mhd_last_modified (r, mtime); + if (verbose > 1) + obatched(clog) << "serving " << metric << " " << b_source0 + << " file " << b_source1 + << " section=" << section + << " IMA signature=" << ima_sig << endl; + /* libmicrohttpd will close fd. */ + } + return r; +} static struct MHD_Response* handle_buildid_r_match (bool internal_req_p, @@ -2142,57 +2217,15 @@ handle_buildid_r_match (bool internal_req_p, break; // branch out of if "loop", to try new libarchive fetch attempt } - if (!section.empty ()) - { - int scn_fd = extract_section (fd, fs.st_mtime, - b_source0 + ":" + b_source1, - section, extract_begin); - close (fd); - if (scn_fd >= 0) - fd = scn_fd; - else - { - if (verbose) - obatched (clog) << "cannot find section " << section - << " for archive " << b_source0 - << " file " << b_source1 << endl; - return 0; - } - - rc = fstat(fd, &fs); - if (rc < 0) - { - close (fd); - throw libc_exception (errno, - string ("fstat archive ") + b_source0 + string (" file ") + b_source1 - + string (" section ") + section); - } - } - - struct MHD_Response* r = MHD_create_response_from_fd (fs.st_size, fd); + struct MHD_Response* r = create_buildid_r_response (b_mtime, b_source0, + b_source1, section, + ima_sig, NULL, fd, + fs.st_size, + fs.st_mtime, + "archive fdcache", + extract_begin); if (r == 0) - { - if (verbose) - obatched(clog) << "cannot create fd-response for " << b_source0 << endl; - close(fd); - break; // branch out of if "loop", to try new libarchive fetch attempt - } - - inc_metric ("http_responses_total","result","archive fdcache"); - - add_mhd_response_header (r, "Content-Type", "application/octet-stream"); - add_mhd_response_header (r, "X-DEBUGINFOD-SIZE", - to_string(fs.st_size).c_str()); - add_mhd_response_header (r, "X-DEBUGINFOD-ARCHIVE", b_source0.c_str()); - add_mhd_response_header (r, "X-DEBUGINFOD-FILE", b_source1.c_str()); - if(!ima_sig.empty()) add_mhd_response_header(r, "X-DEBUGINFOD-IMASIGNATURE", ima_sig.c_str()); - add_mhd_last_modified (r, fs.st_mtime); - if (verbose > 1) - obatched(clog) << "serving fdcache archive " << b_source0 - << " file " << b_source1 - << " section=" << section - << " IMA signature=" << ima_sig << endl; - /* libmicrohttpd will close it. */ + break; // branch out of if "loop", to try new libarchive fetch attempt if (result_fd) *result_fd = fd; return r; @@ -2307,13 +2340,12 @@ handle_buildid_r_match (bool internal_req_p, tvs[1].tv_nsec = archive_entry_mtime_nsec(e); (void) futimens (fd, tvs); /* best effort */ - struct timespec extract_end; - clock_gettime (CLOCK_MONOTONIC, &extract_end); - double extract_time = (extract_end.tv_sec - extract_begin.tv_sec) - + (extract_end.tv_nsec - extract_begin.tv_nsec)/1.e9; - if (r != 0) // stage 3 { + struct timespec extract_end; + clock_gettime (CLOCK_MONOTONIC, &extract_end); + double extract_time = (extract_end.tv_sec - extract_begin.tv_sec) + + (extract_end.tv_nsec - extract_begin.tv_nsec)/1.e9; // NB: now we know we have a complete reusable file; make fdcache // responsible for unlinking it later. fdcache.intern(b_source0, fn, @@ -2324,69 +2356,16 @@ handle_buildid_r_match (bool internal_req_p, continue; } - // NB: now we know we have a complete reusable file; make fdcache - // responsible for unlinking it later. - fdcache.intern(b_source0, b_source1, - tmppath, archive_entry_size(e), - true, extract_time); // requested ones go to the front of the line - - if (!section.empty ()) - { - int scn_fd = extract_section (fd, b_mtime, - b_source0 + ":" + b_source1, - section, extract_begin); - close (fd); - if (scn_fd >= 0) - fd = scn_fd; - else - { - if (verbose) - obatched (clog) << "cannot find section " << section - << " for archive " << b_source0 - << " file " << b_source1 << endl; - return 0; - } - - rc = fstat(fd, &fs); - if (rc < 0) - { - close (fd); - throw libc_exception (errno, - string ("fstat ") + b_source0 + string (" ") + section); - } - r = MHD_create_response_from_fd (fs.st_size, fd); - } - else - r = MHD_create_response_from_fd (archive_entry_size(e), fd); - - inc_metric ("http_responses_total","result",archive_extension + " archive"); + r = create_buildid_r_response (b_mtime, b_source0, b_source1, section, + ima_sig, tmppath, fd, + archive_entry_size(e), + archive_entry_mtime(e), + archive_extension + " archive", + extract_begin); if (r == 0) - { - if (verbose) - obatched(clog) << "cannot create fd-response for " << b_source0 << endl; - close(fd); - break; // assume no chance of better luck around another iteration; no other copies of same file - } - else - { - add_mhd_response_header (r, "Content-Type", - "application/octet-stream"); - add_mhd_response_header (r, "X-DEBUGINFOD-SIZE", - to_string(archive_entry_size(e)).c_str()); - add_mhd_response_header (r, "X-DEBUGINFOD-ARCHIVE", b_source0.c_str()); - add_mhd_response_header (r, "X-DEBUGINFOD-FILE", b_source1.c_str()); - if(!ima_sig.empty()) add_mhd_response_header(r, "X-DEBUGINFOD-IMASIGNATURE", ima_sig.c_str()); - add_mhd_last_modified (r, archive_entry_mtime(e)); - if (verbose > 1) - obatched(clog) << "serving archive " << b_source0 - << " file " << b_source1 - << " section=" << section - << " IMA signature=" << ima_sig << endl; - /* libmicrohttpd will close it. */ - if (result_fd) - *result_fd = fd; - continue; - } + break; // assume no chance of better luck around another iteration; no other copies of same file + if (result_fd) + *result_fd = fd; } // XXX: rpm/file not found: delete this R entry? From patchwork Tue Jul 23 22:35:40 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Omar Sandoval X-Patchwork-Id: 94394 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 9EB573858410 for ; Tue, 23 Jul 2024 22:36:50 +0000 (GMT) X-Original-To: elfutils-devel@sourceware.org Delivered-To: elfutils-devel@sourceware.org Received: from mail-oa1-x31.google.com (mail-oa1-x31.google.com [IPv6:2001:4860:4864:20::31]) by sourceware.org (Postfix) with ESMTPS id 7FECE3858414 for ; Tue, 23 Jul 2024 22:35:59 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 7FECE3858414 Authentication-Results: sourceware.org; dmarc=none (p=none dis=none) header.from=osandov.com Authentication-Results: sourceware.org; spf=none smtp.mailfrom=osandov.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 7FECE3858414 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=2001:4860:4864:20::31 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1721774162; cv=none; b=HppkFPKkh2zEGE1z4rKlj2smx/oujmAXmUSepsN6spCeBxbYXE37TWHe26Js1768zivHjc5ujhteFhoUZE+UcmWoq2azr85ZVtwoiUOuF3UxrqwHzkxRVLWNTcLJXiROg+w3fWHFn/NiKKYpXupDU1x15l57KtrigJoqwjhYINc= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1721774162; c=relaxed/simple; bh=HOfAHJo///BN1FVi73kIPNB998r4isI11ggmhEvmArI=; h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version; b=x4QBSd9aPHh8J7Fegf5ZxXdbZi7IKceGB14B3SycSeid6cpncKmqRYC7DMyAhZ2Cg7HevEenojjgtxSskf/idoJnqafjuSLrKvyhToY6YVLi24QMWBbSrIR1M7/k5iU8agQV+Pr4dV2mnQlrV2gDPADDWPman4mHjpnd9KlcQF0= ARC-Authentication-Results: i=1; server2.sourceware.org Received: by mail-oa1-x31.google.com with SMTP id 586e51a60fabf-260e12aac26so3605595fac.0 for ; Tue, 23 Jul 2024 15:35:59 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=osandov-com.20230601.gappssmtp.com; s=20230601; t=1721774158; x=1722378958; darn=sourceware.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=u4Rj/ZnOVSL02OnzUupYCg3NbyqN5BwrWVZX7PHvJeg=; b=WMjM7Ct5MnrtKZJFyT4948O/cLuPsCICvaOKW2NxG74PhpSREBB/i8ss62pAkjESE/ 33Ob4bAvXSGH3cn0D9gFaKirl7P1ucg/MrHKTPeQ5nfBMGWwsOUHbVKCERtuRZFTPiDu 9eesAtRgHTsYDBgRMk+dBTvMRFd9EQJEZy9E7D6ClnrroYwac/DGDMjtJAvur6WVvRKt tuBwZt1bXVLa9R6BFkkyDFamqZDidB/hlWikYuSaG6bbtUda+9mMrUYi7l/vCdxMUiK4 XbAKsFcTC0qOvx0xdrCP13BtS6GuQWvSh2sEyxAYibiDb7gd016iU7YPer3iWdwMjHZE c0XQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1721774158; x=1722378958; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=u4Rj/ZnOVSL02OnzUupYCg3NbyqN5BwrWVZX7PHvJeg=; b=tk07l0FGnP3F2BQnOFeEdjixZYnal1UkTy6MsL5YeuhC0Uqhikx/KxXxfMaEPD8gCj pMKwqMQvQVNC8FAXubdVMWeazM1NsTnOVdLNy7Gy7n81AsJIqojBKQSLHuq8h6HoOtPZ VnDl6VaXdEAJB0IV7ort1nfs3mxC7PDAe/NYPIUOOyoMGpM4N5iAbPqrRZutKO+EoG0K eoVMKx+yOA7c1JreE73caJL5GgZXGc9GM689aiDpv9MPBwZ0h25xMzChDQMbqkuDTyFb qaNbx2SjeVUl+RM9kQLi9VE3mBhp3cD2hjOFtKTyyRgnzPv5w5jhTltYBI0dGDkYsn9m 6xCg== X-Gm-Message-State: AOJu0YwLG7SI86N+ELXFIXYYpK5qJk3pf9dzNbX1OYJFpwiMszgDNs7K Hn6Elhrv7ZpgpOVqbTEWPN/2FvdQIXY49tGGcMKcqaziPWnyitDDX3EmcCswnyrDeWldrSo8eEV C X-Google-Smtp-Source: AGHT+IHeFnw2y57mOvcjRUIMaso4gzunebmtC6lhrer++vPe7Vr308eHS/nWncqeffgY1EoxwMb7WA== X-Received: by 2002:a05:6871:b08:b0:25e:29e7:14c8 with SMTP id 586e51a60fabf-2648cc9f300mr292136fac.42.1721774158172; Tue, 23 Jul 2024 15:35:58 -0700 (PDT) Received: from telecaster.thefacebook.com ([2620:10d:c090:500::5:b6d3]) by smtp.gmail.com with ESMTPSA id d2e1a72fcca58-70d241803fdsm4308079b3a.220.2024.07.23.15.35.57 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 23 Jul 2024 15:35:57 -0700 (PDT) From: Omar Sandoval To: elfutils-devel@sourceware.org Cc: "Frank Ch . Eigler" , Aaron Merey , linux-debuggers@vger.kernel.org Subject: [PATCH v5 4/7] debugifod: add new table and views for seekable archives Date: Tue, 23 Jul 2024 15:35:40 -0700 Message-ID: <4dd631195fbccd6aaec440f38ff12220ea6cc516.1721773977.git.osandov@fb.com> X-Mailer: git-send-email 2.45.2 In-Reply-To: References: MIME-Version: 1.0 X-Spam-Status: No, score=-12.0 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, GIT_PATCH_0, RCVD_IN_DNSWL_NONE, SPF_HELO_NONE, SPF_NONE, TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: elfutils-devel@sourceware.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Elfutils-devel mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: elfutils-devel-bounces~patchwork=sourceware.org@sourceware.org From: Omar Sandoval In order to extract a file from a seekable archive, we need to know where in the uncompressed archive the file data starts and its size. Additionally, in order to populate the response headers, we need the file modification time (since we won't be able to get it from the archive metadata). Add a new table, _r_seekable, keyed on the archive file id and entry file id and containing the size, offset, and mtime. It also contains the compression type just in case new seekable formats are supported in the future. In order to search this table when we get a request, we need the file ids available. Add the ids to the _query_d and _query_e views, and rename them to _query_d2 and _query_e2. This schema change is backward compatible and doesn't require reindexing. _query_d2 and _query_e2 can be renamed back the next time BUILDIDS needs to be bumped. Before this change, the database for a single kernel debuginfo RPM (kernel-debuginfo-6.9.6-200.fc40.x86_64.rpm) was about 15MB. This change increases that by about 70kB, only a 0.5% increase. Signed-off-by: Omar Sandoval --- debuginfod/debuginfod.cxx | 34 ++++++++++++++++++++++++---------- 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/debuginfod/debuginfod.cxx b/debuginfod/debuginfod.cxx index 24702c23..b3d80090 100644 --- a/debuginfod/debuginfod.cxx +++ b/debuginfod/debuginfod.cxx @@ -265,25 +265,39 @@ static const char DEBUGINFOD_SQLITE_DDL[] = " foreign key (content) references " BUILDIDS "_files(id) on update cascade on delete cascade,\n" " primary key (content, file, mtime)\n" " ) " WITHOUT_ROWID ";\n" + "create table if not exists " BUILDIDS "_r_seekable (\n" // seekable rpm contents + " file integer not null,\n" + " content integer not null,\n" + " type text not null,\n" + " size integer not null,\n" + " offset integer not null,\n" + " mtime integer not null,\n" + " foreign key (file) references " BUILDIDS "_files(id) on update cascade on delete cascade,\n" + " foreign key (content) references " BUILDIDS "_files(id) on update cascade on delete cascade,\n" + " primary key (file, content)\n" + " ) " WITHOUT_ROWID ";\n" // create views to glue together some of the above tables, for webapi D queries - "create view if not exists " BUILDIDS "_query_d as \n" + // NB: _query_d2 and _query_e2 were added to replace _query_d and _query_e + // without updating BUILDIDS. They can be renamed back the next time BUILDIDS + // is updated. + "create view if not exists " BUILDIDS "_query_d2 as \n" "select\n" - " b.hex as buildid, n.mtime, 'F' as sourcetype, f0.name as source0, n.mtime as mtime, null as source1\n" + " b.hex as buildid, 'F' as sourcetype, n.file as id0, f0.name as source0, n.mtime as mtime, null as id1, null as source1\n" " from " BUILDIDS "_buildids b, " BUILDIDS "_files_v f0, " BUILDIDS "_f_de n\n" " where b.id = n.buildid and f0.id = n.file and n.debuginfo_p = 1\n" "union all select\n" - " b.hex as buildid, n.mtime, 'R' as sourcetype, f0.name as source0, n.mtime as mtime, f1.name as source1\n" + " b.hex as buildid, 'R' as sourcetype, n.file as id0, f0.name as source0, n.mtime as mtime, n.content as id1, f1.name as source1\n" " from " BUILDIDS "_buildids b, " BUILDIDS "_files_v f0, " BUILDIDS "_files_v f1, " BUILDIDS "_r_de n\n" " where b.id = n.buildid and f0.id = n.file and f1.id = n.content and n.debuginfo_p = 1\n" ";" // ... and for E queries - "create view if not exists " BUILDIDS "_query_e as \n" + "create view if not exists " BUILDIDS "_query_e2 as \n" "select\n" - " b.hex as buildid, n.mtime, 'F' as sourcetype, f0.name as source0, n.mtime as mtime, null as source1\n" + " b.hex as buildid, 'F' as sourcetype, n.file as id0, f0.name as source0, n.mtime as mtime, null as id1, null as source1\n" " from " BUILDIDS "_buildids b, " BUILDIDS "_files_v f0, " BUILDIDS "_f_de n\n" " where b.id = n.buildid and f0.id = n.file and n.executable_p = 1\n" "union all select\n" - " b.hex as buildid, n.mtime, 'R' as sourcetype, f0.name as source0, n.mtime as mtime, f1.name as source1\n" + " b.hex as buildid, 'R' as sourcetype, n.file as id0, f0.name as source0, n.mtime as mtime, n.content as id1, f1.name as source1\n" " from " BUILDIDS "_buildids b, " BUILDIDS "_files_v f0, " BUILDIDS "_files_v f1, " BUILDIDS "_r_de n\n" " where b.id = n.buildid and f0.id = n.file and f1.id = n.content and n.executable_p = 1\n" ";" @@ -2557,7 +2571,7 @@ handle_buildid (MHD_Connection* conn, if (atype_code == "D") { pp = new sqlite_ps (thisdb, "mhd-query-d", - "select mtime, sourcetype, source0, source1 from " BUILDIDS "_query_d where buildid = ? " + "select mtime, sourcetype, source0, source1 from " BUILDIDS "_query_d2 where buildid = ? " "order by mtime desc"); pp->reset(); pp->bind(1, buildid); @@ -2565,7 +2579,7 @@ handle_buildid (MHD_Connection* conn, else if (atype_code == "E") { pp = new sqlite_ps (thisdb, "mhd-query-e", - "select mtime, sourcetype, source0, source1 from " BUILDIDS "_query_e where buildid = ? " + "select mtime, sourcetype, source0, source1 from " BUILDIDS "_query_e2 where buildid = ? " "order by mtime desc"); pp->reset(); pp->bind(1, buildid); @@ -2589,9 +2603,9 @@ handle_buildid (MHD_Connection* conn, else if (atype_code == "I") { pp = new sqlite_ps (thisdb, "mhd-query-i", - "select mtime, sourcetype, source0, source1, 1 as debug_p from " BUILDIDS "_query_d where buildid = ? " + "select mtime, sourcetype, source0, source1, 1 as debug_p from " BUILDIDS "_query_d2 where buildid = ? " "union all " - "select mtime, sourcetype, source0, source1, 0 as debug_p from " BUILDIDS "_query_e where buildid = ? " + "select mtime, sourcetype, source0, source1, 0 as debug_p from " BUILDIDS "_query_e2 where buildid = ? " "order by debug_p desc, mtime desc"); pp->reset(); pp->bind(1, buildid); From patchwork Tue Jul 23 22:35:41 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Omar Sandoval X-Patchwork-Id: 94395 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id AF76F3858404 for ; Tue, 23 Jul 2024 22:37:01 +0000 (GMT) X-Original-To: elfutils-devel@sourceware.org Delivered-To: elfutils-devel@sourceware.org Received: from mail-pf1-x433.google.com (mail-pf1-x433.google.com [IPv6:2607:f8b0:4864:20::433]) by sourceware.org (Postfix) with ESMTPS id 4599D385842D for ; Tue, 23 Jul 2024 22:36:01 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 4599D385842D Authentication-Results: sourceware.org; dmarc=none (p=none dis=none) header.from=osandov.com Authentication-Results: sourceware.org; spf=none smtp.mailfrom=osandov.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 4599D385842D Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=2607:f8b0:4864:20::433 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1721774165; cv=none; b=jv1htBcLoBy/uhLU3SGv7wqKQtFXnuzP/bBmJrKOnZRH9nZMOU0w2IdzAbRF+2NLmNwoTHvoLTdFZe08aGAo8KV0mMChC27OiP5ylk6oiqLJiNOsRzOVFvmRdJczPR4T/LPwEdLwa+jp6HLlcewsPzH4gVhuNNtK9tMGV9ZJtf0= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1721774165; c=relaxed/simple; bh=iqlG0Dw0ZyEWzSwxSuMLFJ/KuSCsEWFd60/jwbF77co=; h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version; b=X+yaGvn1XyzebKXzMcf1FrzERy9lNI+nTwmsYNgUdAWx38Zy7dIDF867fj+kZivVUtVRs/RPNAA05sI5bkP15osk/MxbBh+428w0tgzQdAImJ4hKI5UMJ+RC0xk8tyYPW9b/jJT95Cm2NlqPxQUdj1UrX3cGMgwiM8W5lhG1brY= ARC-Authentication-Results: i=1; server2.sourceware.org Received: by mail-pf1-x433.google.com with SMTP id d2e1a72fcca58-70d357040dbso1455615b3a.1 for ; Tue, 23 Jul 2024 15:36:01 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=osandov-com.20230601.gappssmtp.com; s=20230601; t=1721774160; x=1722378960; darn=sourceware.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=5C+Pjur69r6z6WOUVFyclMKRmO7Dq0GkyE/j9S93y1I=; b=OdoepoxVDF0UaE05eWTKipCtXVJSs0ERSOp/vA59gbQVgVn7qzMKc5anrXxG6p20ca EvQlJgkv26SvZaRtCn4opAGNd3mOXog8quurVef5umDbD5iYvSQXHdUr8NrjLby6bdu/ q1LpzfJXPqyzNkNKYqak8WG++0WQ8ZoGZvQiiSiuTBzmK5rCKTS+RA/K7CvMAzlFF1Xk mQ7Q97yF19vo86+phxrmXS+J1qSG/vFu1wZhOXPZKxx3k22zD9MLrRX7JW01yRPaXUjS ts+NHYzYpFh3nCaxUkXuKuK2O8FfuWUfCP9soezKEk7Cm6w07tPGj6px/e1upvOqifum kNoA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1721774160; x=1722378960; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=5C+Pjur69r6z6WOUVFyclMKRmO7Dq0GkyE/j9S93y1I=; b=FlcGvDw8BULMdQLJtQiTbRC64GNYy0eOsZxWSsBrYzmBV0rXCn1oaKH6gQkVZpuc5U idxqRtzyTUjfYF4KT4tqLS77sCLDnb2pzdyQbZibIYOwCRrC7uWEpvZuvR8F7CudhiwI EQ88lChIkK1MGGK8ebf5BNQW0oJEbIicTjY9FbbTzWPLIvZjJpOsXkoiu/enlMa6fghf lPA36nugnqd4/8LF5JXEd3ASLWJGnDAtX+pdXz6VL7FSgqzsd9RkCFs0Dpi4yoj64p2v f1GVy8tljoevdz8u6QNRVahxE+ovo4K4TN+H7vKXKxR5un/rg3kUtPa4Kvy3b1BKuKBf rgZw== X-Gm-Message-State: AOJu0YzRts7Ycpdi3w5EQjcPzA/5UzDzLliv8RaJTSomMutWD0ZcOdwd 9c47MPukCVsFrVI47Iq6D1KqxwU/ukHD02A6W1TRjh3ZarivgQ4D9WKg8QEjFpDISzGHBjkNFj3 2 X-Google-Smtp-Source: AGHT+IH9KqbdfDSm9mRwpAS98n2B5zgHfwqJNB0CLZdR9FdFQnjubg7P22vysBxYz9KbYg+3hT0adQ== X-Received: by 2002:a05:6a21:3283:b0:1c0:e46a:1639 with SMTP id adf61e73a8af0-1c46196d145mr616745637.19.1721774159546; Tue, 23 Jul 2024 15:35:59 -0700 (PDT) Received: from telecaster.thefacebook.com ([2620:10d:c090:500::5:b6d3]) by smtp.gmail.com with ESMTPSA id d2e1a72fcca58-70d241803fdsm4308079b3a.220.2024.07.23.15.35.58 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 23 Jul 2024 15:35:58 -0700 (PDT) From: Omar Sandoval To: elfutils-devel@sourceware.org Cc: "Frank Ch . Eigler" , Aaron Merey , linux-debuggers@vger.kernel.org Subject: [PATCH v5 5/7] debuginfod: optimize extraction from seekable xz archives Date: Tue, 23 Jul 2024 15:35:41 -0700 Message-ID: <967aa58cfdff12734036216e17f5fea8e5c45540.1721773977.git.osandov@fb.com> X-Mailer: git-send-email 2.45.2 In-Reply-To: References: MIME-Version: 1.0 X-Spam-Status: No, score=-12.1 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, GIT_PATCH_0, RCVD_IN_DNSWL_NONE, SPF_HELO_NONE, SPF_NONE, TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: elfutils-devel@sourceware.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Elfutils-devel mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: elfutils-devel-bounces~patchwork=sourceware.org@sourceware.org From: Omar Sandoval The kernel debuginfo packages on Fedora, Debian, and Ubuntu, and many of their downstreams, are all compressed with xz in multi-threaded mode, which allows random access. We can use this to bypass the full archive extraction and dramatically speed up kernel debuginfo requests (from ~50 seconds in the worst case to < 0.25 seconds). This works because multi-threaded xz compression splits up the stream into many independently compressed blocks. The stream ends with an index of blocks. So, to seek to an offset, we find the block containing that offset in the index and then decompress and throw away data until we reach the offset within the block. We can then decompress the desired amount of data, possibly from subsequent blocks. There's no high-level API in liblzma to do this, but we can do it by stitching together a few low-level APIs. We need to pass down the file ids then look up the size, uncompressed offset, and mtime in the _r_seekable table. Note that this table is not yet populated, so this commit has no functional change on its own. Signed-off-by: Omar Sandoval --- configure.ac | 5 + debuginfod/Makefile.am | 2 +- debuginfod/debuginfod.cxx | 456 +++++++++++++++++++++++++++++++++++++- 3 files changed, 457 insertions(+), 6 deletions(-) diff --git a/configure.ac b/configure.ac index 24e68d94..9c5f7e51 100644 --- a/configure.ac +++ b/configure.ac @@ -441,8 +441,13 @@ eu_ZIPLIB(bzlib,BZLIB,bz2,BZ2_bzdopen,bzip2) # We need this since bzip2 doesn't have a pkgconfig file. BZ2_LIB="$LIBS" AC_SUBST([BZ2_LIB]) +save_LIBS="$LIBS" +LIBS= eu_ZIPLIB(lzma,LZMA,lzma,lzma_auto_decoder,[LZMA (xz)]) +lzma_LIBS="$LIBS" +LIBS="$lzma_LIBS $save_LIBS" AS_IF([test "x$with_lzma" = xyes], [LIBLZMA="liblzma"], [LIBLZMA=""]) +AC_SUBST([lzma_LIBS]) AC_SUBST([LIBLZMA]) eu_ZIPLIB(zstd,ZSTD,zstd,ZSTD_decompress,[ZSTD (zst)]) AS_IF([test "x$with_zstd" = xyes], [LIBZSTD="libzstd"], [LIBLZSTD=""]) diff --git a/debuginfod/Makefile.am b/debuginfod/Makefile.am index b74e3673..e199dc0c 100644 --- a/debuginfod/Makefile.am +++ b/debuginfod/Makefile.am @@ -70,7 +70,7 @@ bin_PROGRAMS += debuginfod-find endif debuginfod_SOURCES = debuginfod.cxx -debuginfod_LDADD = $(libdw) $(libelf) $(libeu) $(libdebuginfod) $(argp_LDADD) $(fts_LIBS) $(libmicrohttpd_LIBS) $(sqlite3_LIBS) $(libarchive_LIBS) $(rpm_LIBS) $(jsonc_LIBS) $(libcurl_LIBS) -lpthread -ldl +debuginfod_LDADD = $(libdw) $(libelf) $(libeu) $(libdebuginfod) $(argp_LDADD) $(fts_LIBS) $(libmicrohttpd_LIBS) $(sqlite3_LIBS) $(libarchive_LIBS) $(rpm_LIBS) $(jsonc_LIBS) $(libcurl_LIBS) $(lzma_LIBS) -lpthread -ldl debuginfod_find_SOURCES = debuginfod-find.c debuginfod_find_LDADD = $(libdw) $(libelf) $(libeu) $(libdebuginfod) $(argp_LDADD) $(fts_LIBS) $(jsonc_LIBS) diff --git a/debuginfod/debuginfod.cxx b/debuginfod/debuginfod.cxx index b3d80090..cf7f48ab 100644 --- a/debuginfod/debuginfod.cxx +++ b/debuginfod/debuginfod.cxx @@ -63,6 +63,10 @@ extern "C" { #undef __attribute__ /* glibc bug - rhbz 1763325 */ #endif +#ifdef USE_LZMA +#include +#endif + #include #include #include @@ -1961,6 +1965,385 @@ handle_buildid_f_match (bool internal_req_t, return r; } + +#ifdef USE_LZMA +struct lzma_exception: public reportable_exception +{ + lzma_exception(int rc, const string& msg): + // liblzma doesn't have a lzma_ret -> string conversion function, so just + // report the value. + reportable_exception(string ("lzma error: ") + msg + ": error " + to_string(rc)) { + inc_metric("error_count","lzma",to_string(rc)); + } +}; + +// Neither RPM nor deb files support seeking to a specific file in the package. +// Instead, to extract a specific file, we normally need to read the archive +// sequentially until we find the file. This is very slow for files at the end +// of a large package with lots of files, like kernel debuginfo. +// +// However, if the compression format used in the archive supports seeking, we +// can accelerate this. As of July 2024, xz is the only widely-used format that +// supports seeking, and usually only in multi-threaded mode. Luckily, the +// kernel-debuginfo package in Fedora and its downstreams, and the +// linux-image-*-dbg package in Debian and its downstreams, all happen to use +// this. +// +// The xz format [1] ends with an index of independently compressed blocks in +// the stream. In RPM and deb files, the xz stream is the last thing in the +// file, so we assume that the xz Stream Footer is at the end of the package +// file and do everything relative to that. For each file in the archive, we +// remember the size and offset of the file data in the uncompressed xz stream, +// then we use the index to seek to that offset when we need that file. +// +// 1: https://xz.tukaani.org/format/xz-file-format.txt + +// Read the Index at the end of an xz file. +static lzma_index* +read_xz_index (int fd) +{ + off_t footer_pos = -LZMA_STREAM_HEADER_SIZE; + if (lseek (fd, footer_pos, SEEK_END) == -1) + throw libc_exception (errno, "lseek"); + + uint8_t footer[LZMA_STREAM_HEADER_SIZE]; + size_t footer_read = 0; + while (footer_read < sizeof (footer)) + { + ssize_t bytes_read = read (fd, footer + footer_read, + sizeof (footer) - footer_read); + if (bytes_read < 0) + { + if (errno == EINTR) + continue; + throw libc_exception (errno, "read"); + } + if (bytes_read == 0) + throw reportable_exception ("truncated file"); + footer_read += bytes_read; + } + + lzma_stream_flags stream_flags; + lzma_ret ret = lzma_stream_footer_decode (&stream_flags, footer); + if (ret != LZMA_OK) + throw lzma_exception (ret, "lzma_stream_footer_decode"); + + if (lseek (fd, footer_pos - stream_flags.backward_size, SEEK_END) == -1) + throw libc_exception (errno, "lseek"); + + lzma_stream strm = LZMA_STREAM_INIT; + lzma_index* index = NULL; + ret = lzma_index_decoder (&strm, &index, UINT64_MAX); + if (ret != LZMA_OK) + throw lzma_exception (ret, "lzma_index_decoder"); + defer_dtor strm_ender (&strm, lzma_end); + + uint8_t in_buf[4096]; + while (true) + { + if (strm.avail_in == 0) + { + ssize_t bytes_read = read (fd, in_buf, sizeof (in_buf)); + if (bytes_read < 0) + { + if (errno == EINTR) + continue; + throw libc_exception (errno, "read"); + } + if (bytes_read == 0) + throw reportable_exception ("truncated file"); + strm.avail_in = bytes_read; + strm.next_in = in_buf; + } + + ret = lzma_code (&strm, LZMA_RUN); + if (ret == LZMA_STREAM_END) + break; + else if (ret != LZMA_OK) + throw lzma_exception (ret, "lzma_code index"); + } + + ret = lzma_index_stream_flags (index, &stream_flags); + if (ret != LZMA_OK) + { + lzma_index_end (index, NULL); + throw lzma_exception (ret, "lzma_index_stream_flags"); + } + return index; +} + +static void +my_lzma_index_end (lzma_index* index) +{ + lzma_index_end (index, NULL); +} + +static void +free_lzma_block_filter_options (lzma_block* block) +{ + for (int i = 0; i < LZMA_FILTERS_MAX; i++) + { + free (block->filters[i].options); + block->filters[i].options = NULL; + } +} + +static void +free_lzma_block_filters (lzma_block* block) +{ + if (block->filters != NULL) + { + free_lzma_block_filter_options (block); + free (block->filters); + } +} + +static void +extract_xz_blocks_into_fd (const string& srcpath, + int src, + int dst, + lzma_index_iter* iter, + uint64_t offset, + uint64_t size) +{ + // Seek to the Block. Seeking from the end using the compressed size from the + // footer means we don't need to know where the xz stream starts in the + // archive. + if (lseek (src, + (off_t) iter->block.compressed_stream_offset + - (off_t) iter->stream.compressed_size, + SEEK_END) == -1) + throw libc_exception (errno, "lseek"); + + offset -= iter->block.uncompressed_file_offset; + + lzma_block block{}; + block.filters = (lzma_filter*) calloc (LZMA_FILTERS_MAX + 1, + sizeof (lzma_filter)); + if (block.filters == NULL) + throw libc_exception (ENOMEM, "cannot allocate lzma_block filters"); + defer_dtor filters_freer (&block, free_lzma_block_filters); + + uint8_t in_buf[4096]; + uint8_t out_buf[4096]; + size_t header_read = 0; + bool need_log_extracting = verbose > 3; + while (true) + { + // The first byte of the Block is the encoded Block Header Size. Read the + // first byte and whatever extra fits in the buffer. + while (header_read == 0) + { + ssize_t bytes_read = read (src, in_buf, sizeof (in_buf)); + if (bytes_read < 0) + { + if (errno == EINTR) + continue; + throw libc_exception (errno, "read"); + } + if (bytes_read == 0) + throw reportable_exception ("truncated file"); + header_read += bytes_read; + } + + block.header_size = lzma_block_header_size_decode (in_buf[0]); + + // If we didn't buffer the whole Block Header earlier, get the rest. + eu_static_assert (sizeof (in_buf) + >= lzma_block_header_size_decode (UINT8_MAX)); + while (header_read < block.header_size) + { + ssize_t bytes_read = read (src, in_buf + header_read, + sizeof (in_buf) - header_read); + if (bytes_read < 0) + { + if (errno == EINTR) + continue; + throw libc_exception (errno, "read"); + } + if (bytes_read == 0) + throw reportable_exception ("truncated file"); + header_read += bytes_read; + } + + // Decode the Block Header. + block.check = iter->stream.flags->check; + lzma_ret ret = lzma_block_header_decode (&block, NULL, in_buf); + if (ret != LZMA_OK) + throw lzma_exception (ret, "lzma_block_header_decode"); + ret = lzma_block_compressed_size (&block, iter->block.unpadded_size); + if (ret != LZMA_OK) + throw lzma_exception (ret, "lzma_block_compressed_size"); + + // Start decoding the Block data. + lzma_stream strm = LZMA_STREAM_INIT; + ret = lzma_block_decoder (&strm, &block); + if (ret != LZMA_OK) + throw lzma_exception (ret, "lzma_block_decoder"); + defer_dtor strm_ender (&strm, lzma_end); + + // We might still have some input buffered from when we read the header. + strm.avail_in = header_read - block.header_size; + strm.next_in = in_buf + block.header_size; + strm.avail_out = sizeof (out_buf); + strm.next_out = out_buf; + while (true) + { + if (strm.avail_in == 0) + { + ssize_t bytes_read = read (src, in_buf, sizeof (in_buf)); + if (bytes_read < 0) + { + if (errno == EINTR) + continue; + throw libc_exception (errno, "read"); + } + if (bytes_read == 0) + throw reportable_exception ("truncated file"); + strm.avail_in = bytes_read; + strm.next_in = in_buf; + } + + ret = lzma_code (&strm, LZMA_RUN); + if (ret != LZMA_OK && ret != LZMA_STREAM_END) + throw lzma_exception (ret, "lzma_code block"); + + // Throw away anything we decode until we reach the offset, then + // start writing to the destination. + if (strm.total_out > offset) + { + size_t bytes_to_write = strm.next_out - out_buf; + uint8_t* buf_to_write = out_buf; + + // Ignore anything in the buffer before the offset. + if (bytes_to_write > strm.total_out - offset) + { + buf_to_write += bytes_to_write - (strm.total_out - offset); + bytes_to_write = strm.total_out - offset; + } + + // Ignore anything after the size. + if (strm.total_out - offset >= size) + bytes_to_write -= strm.total_out - offset - size; + + if (need_log_extracting) + { + obatched(clog) << "extracting from xz archive " << srcpath + << " size=" << size << endl; + need_log_extracting = false; + } + + while (bytes_to_write > 0) + { + ssize_t written = write (dst, buf_to_write, bytes_to_write); + if (written < 0) + { + if (errno == EAGAIN) + continue; + throw libc_exception (errno, "write"); + } + bytes_to_write -= written; + buf_to_write += written; + } + + // If we reached the size, we're done. + if (strm.total_out - offset >= size) + return; + } + + strm.avail_out = sizeof (out_buf); + strm.next_out = out_buf; + + if (ret == LZMA_STREAM_END) + break; + } + + // This Block didn't have enough data. Go to the next one. + if (lzma_index_iter_next (iter, LZMA_INDEX_ITER_BLOCK)) + throw reportable_exception ("no more blocks"); + if (strm.total_out > offset) + size -= strm.total_out - offset; + offset = 0; + // If we had any buffered input left, move it to the beginning of the + // buffer to decode the next Block Header. + if (strm.avail_in > 0) + { + memmove (in_buf, strm.next_in, strm.avail_in); + header_read = strm.avail_in; + } + else + header_read = 0; + free_lzma_block_filter_options (&block); + } +} + +static int +extract_from_seekable_archive (const string& srcpath, + char* tmppath, + uint64_t offset, + uint64_t size) +{ + inc_metric ("seekable_archive_extraction_attempts","type","xz"); + try + { + int src = open (srcpath.c_str(), O_RDONLY); + if (src < 0) + throw libc_exception (errno, string("open ") + srcpath); + defer_dtor src_closer (src, close); + + lzma_index* index = read_xz_index (src); + defer_dtor index_ender (index, my_lzma_index_end); + + // Find the Block containing the offset. + lzma_index_iter iter; + lzma_index_iter_init (&iter, index); + if (lzma_index_iter_locate (&iter, offset)) + throw reportable_exception ("offset not found"); + + if (verbose > 3) + obatched(clog) << "seeking in xz archive " << srcpath + << " offset=" << offset << " block_offset=" + << iter.block.uncompressed_file_offset << endl; + + int dst = mkstemp (tmppath); + if (dst < 0) + throw libc_exception (errno, "cannot create temporary file"); + + try + { + extract_xz_blocks_into_fd (srcpath, src, dst, &iter, offset, size); + } + catch (...) + { + unlink (tmppath); + close (dst); + throw; + } + + inc_metric ("seekable_archive_extraction_successes","type","xz"); + return dst; + } + catch (const reportable_exception &e) + { + inc_metric ("seekable_archive_extraction_failures","type","xz"); + if (verbose) + obatched(clog) << "failed to extract from seekable xz archive " + << srcpath << ": " << e.message << endl; + return -1; + } +} +#else +static int +extract_from_seekable_archive (const string& srcpath, + char* tmppath, + uint64_t offset, + uint64_t size) +{ + return -1; +} +#endif + + // For security/portability reasons, many distro-package archives have // a "./" in front of path names; others have nothing, others have // "/". Canonicalize them all to a single leading "/", with the @@ -2060,6 +2443,8 @@ handle_buildid_r_match (bool internal_req_p, int64_t b_mtime, const string& b_source0, const string& b_source1, + int64_t b_id0, + int64_t b_id1, const string& section, int *result_fd) { @@ -2246,7 +2631,59 @@ handle_buildid_r_match (bool internal_req_p, // NB: see, we never go around the 'loop' more than once } - // no match ... grumble, must process the archive + // no match ... look for a seekable entry + unique_ptr pp (new sqlite_ps (internal_req_p ? db : dbq, + "rpm-seekable-query", + "select type, size, offset, mtime from " BUILDIDS "_r_seekable " + "where file = ? and content = ?")); + rc = pp->reset().bind(1, b_id0).bind(2, b_id1).step(); + if (rc != SQLITE_DONE) + { + if (rc != SQLITE_ROW) + throw sqlite_exception(rc, "step"); + const char* seekable_type = (const char*) sqlite3_column_text (*pp, 0); + if (seekable_type != NULL && strcmp (seekable_type, "xz") == 0) + { + int64_t seekable_size = sqlite3_column_int64 (*pp, 1); + int64_t seekable_offset = sqlite3_column_int64 (*pp, 2); + int64_t seekable_mtime = sqlite3_column_int64 (*pp, 3); + + char* tmppath = NULL; + if (asprintf (&tmppath, "%s/debuginfod-fdcache.XXXXXX", tmpdir.c_str()) < 0) + throw libc_exception (ENOMEM, "cannot allocate tmppath"); + defer_dtor tmmpath_freer (tmppath, free); + + fd = extract_from_seekable_archive (b_source0, tmppath, + seekable_offset, seekable_size); + if (fd >= 0) + { + // Set the mtime so the fdcache file mtimes propagate to future webapi + // clients. + struct timespec tvs[2]; + tvs[0].tv_sec = 0; + tvs[0].tv_nsec = UTIME_OMIT; + tvs[1].tv_sec = seekable_mtime; + tvs[1].tv_nsec = 0; + (void) futimens (fd, tvs); /* best effort */ + struct MHD_Response* r = create_buildid_r_response (b_mtime, + b_source0, + b_source1, + section, + ima_sig, + tmppath, fd, + seekable_size, + seekable_mtime, + "seekable xz archive", + extract_begin); + if (r != 0 && result_fd) + *result_fd = fd; + return r; + } + } + } + pp.reset(); + + // still no match ... grumble, must process the archive string archive_decoder = "/dev/null"; string archive_extension = ""; for (auto&& arch : scan_archives) @@ -2445,6 +2882,8 @@ handle_buildid_match (bool internal_req_p, const string& b_stype, const string& b_source0, const string& b_source1, + int64_t b_id0, + int64_t b_id1, const string& section, int *result_fd) { @@ -2455,7 +2894,8 @@ handle_buildid_match (bool internal_req_p, section, result_fd); else if (b_stype == "R") return handle_buildid_r_match(internal_req_p, b_mtime, b_source0, - b_source1, section, result_fd); + b_source1, b_id0, b_id1, section, + result_fd); } catch (const reportable_exception &e) { @@ -2571,7 +3011,7 @@ handle_buildid (MHD_Connection* conn, if (atype_code == "D") { pp = new sqlite_ps (thisdb, "mhd-query-d", - "select mtime, sourcetype, source0, source1 from " BUILDIDS "_query_d2 where buildid = ? " + "select mtime, sourcetype, source0, source1, id0, id1 from " BUILDIDS "_query_d2 where buildid = ? " "order by mtime desc"); pp->reset(); pp->bind(1, buildid); @@ -2579,7 +3019,7 @@ handle_buildid (MHD_Connection* conn, else if (atype_code == "E") { pp = new sqlite_ps (thisdb, "mhd-query-e", - "select mtime, sourcetype, source0, source1 from " BUILDIDS "_query_e2 where buildid = ? " + "select mtime, sourcetype, source0, source1, id0, id1 from " BUILDIDS "_query_e2 where buildid = ? " "order by mtime desc"); pp->reset(); pp->bind(1, buildid); @@ -2627,6 +3067,12 @@ handle_buildid (MHD_Connection* conn, string b_stype = string((const char*) sqlite3_column_text (*pp, 1) ?: ""); /* by DDL may not be NULL */ string b_source0 = string((const char*) sqlite3_column_text (*pp, 2) ?: ""); /* may be NULL */ string b_source1 = string((const char*) sqlite3_column_text (*pp, 3) ?: ""); /* may be NULL */ + int64_t b_id0 = 0, b_id1 = 0; + if (atype_code == "D" || atype_code == "E") + { + b_id0 = sqlite3_column_int64 (*pp, 4); + b_id1 = sqlite3_column_int64 (*pp, 5); + } if (verbose > 1) obatched(clog) << "found mtime=" << b_mtime << " stype=" << b_stype @@ -2636,7 +3082,7 @@ handle_buildid (MHD_Connection* conn, // XXX: in case of multiple matches, attempt them in parallel? auto r = handle_buildid_match (conn ? false : true, b_mtime, b_stype, b_source0, b_source1, - section, result_fd); + b_id0, b_id1, section, result_fd); if (r) return r; From patchwork Tue Jul 23 22:35:42 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Omar Sandoval X-Patchwork-Id: 94393 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 81C5E3858CD9 for ; Tue, 23 Jul 2024 22:36:40 +0000 (GMT) X-Original-To: elfutils-devel@sourceware.org Delivered-To: elfutils-devel@sourceware.org Received: from mail-pf1-x42b.google.com (mail-pf1-x42b.google.com [IPv6:2607:f8b0:4864:20::42b]) by sourceware.org (Postfix) with ESMTPS id 08A133858C56 for ; Tue, 23 Jul 2024 22:36:05 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 08A133858C56 Authentication-Results: sourceware.org; dmarc=none (p=none dis=none) header.from=osandov.com Authentication-Results: sourceware.org; spf=none smtp.mailfrom=osandov.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 08A133858C56 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=2607:f8b0:4864:20::42b ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1721774173; cv=none; b=VEN71AD0r3y+tpaeZVmrcdslORJ3xMePnLtYD2tqf4UjPIZThiwnWgp8YGjhbHM6yoMIaVh7qSD7/fcW3Ei8PvUIIZusdx5gyu1iVuME4jNafCYIpOZvL91WckdEfxrjOaSU8alW10QzED3yfRg4BiGCXKlkeBbLWdLdVfyokZs= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1721774173; c=relaxed/simple; bh=gzK070Donkm8ClzO8ZQqi/gmuy3kLeVGupjX6jMY5hc=; h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version; b=ujVeVqRSIdxe3Not4fc7GEGN5NpD6qXKfhuTi94OS8Bx92pxxblxRjPGgTGdyy7PmkaATx28BZZPh9WZHtIEOnaHiEaRZb3t3umIuv3lbzGuI/6hrwjkmW45cz6Kxu0zx3EQwBiTa2AvqbOtn1cmFrNXHrIzbT0SAIeQ3Xv5PRs= ARC-Authentication-Results: i=1; server2.sourceware.org Received: by mail-pf1-x42b.google.com with SMTP id d2e1a72fcca58-70d2e68f5a8so1713446b3a.3 for ; Tue, 23 Jul 2024 15:36:04 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=osandov-com.20230601.gappssmtp.com; s=20230601; t=1721774163; x=1722378963; darn=sourceware.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=pu7UnMwBGeRk2rIdFyh2KDlZYo8YF8ivpg0CCfxUC5g=; b=tlC6UTZ8qOSnD2DZl6j0wS7EqyOA1mtCmyBJ6s63of0aOSBHiKb3nv8AYwyYeN8kzf tunD1SzVLQ2/D72ZeNLhei9tmHK5V4ESpueANbN+ON7JuN5a2Qmr3byZzOJB5FkrSQMZ scxPr7Wzv4NOX5Ht8sxtrIpq72uP/rW3P0X502z5mDv/9eub32SIzeEVrUYMCHlGYJ8X Q1WhJb8BYbrhP/iR+AroiGEdAzH5kcHXkJhfx/z5MauORpy3t4vp+8k3WpwKaSPFdOkT DqFF1tzN8rvBlOV/rM9ZWGSNnuBN/832gbiYtMPNQKFfEdZ7wTAV4elvq1bMn4IcxPOi ONUQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1721774163; x=1722378963; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=pu7UnMwBGeRk2rIdFyh2KDlZYo8YF8ivpg0CCfxUC5g=; b=sp90BpDbT7MSRboBGP2iB85b2sggb2URFHiuFNFO090/R0ogWka5otqn5n/mgvysvY ndtWIZqyZqSKRZxsGNEL/FdYIuk4G1VzUD4XQw7pMaNvPrhvAJYYhANBvIk2szRSz7oH 88slzoCnzfO1I2gQF7bESAIfho0Zm6FOUiaqDsuAagZq2jFF4JttH85t0E0tlxXF6e0s KJDKrR22wyEyEiWLzmSNH0eKT1qX3oeNEKI2N95FJOYaJVMsv8y5kmAFZeI++ac1V1X9 T3P/2wGR06zPm2uc37jSsg3XCXI4oLIJymRmuktqWHj6dxpRKGkVRoR5+1z1UhdY7uw8 /buA== X-Gm-Message-State: AOJu0YxMy46mTaBws0fmRqYDm7cvqDVZX9lrkco5EdXIJZQ8B7yyKjOs uLmPqdJlGiESYK9GbLik9WiFdpnRXwyuxmqDO6obq7oQgHf/B/7ZXhb5LW1Rv15zcEf3WvPhpPt x X-Google-Smtp-Source: AGHT+IGUZ/YLbT4SHQt8IN8hSo4wC7DHHDDkzUG1xxf0VXzBGPvpm6TZF4G/Jml5kJc0P4C6Jaq8bg== X-Received: by 2002:a05:6a20:cfa3:b0:1be:e6d8:756e with SMTP id adf61e73a8af0-1c4229a9b53mr16486299637.53.1721774161399; Tue, 23 Jul 2024 15:36:01 -0700 (PDT) Received: from telecaster.thefacebook.com ([2620:10d:c090:500::5:b6d3]) by smtp.gmail.com with ESMTPSA id d2e1a72fcca58-70d241803fdsm4308079b3a.220.2024.07.23.15.35.59 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 23 Jul 2024 15:36:00 -0700 (PDT) From: Omar Sandoval To: elfutils-devel@sourceware.org Cc: "Frank Ch . Eigler" , Aaron Merey , linux-debuggers@vger.kernel.org Subject: [PATCH v5 6/7] debuginfod: populate _r_seekable on scan Date: Tue, 23 Jul 2024 15:35:42 -0700 Message-ID: <2a0f71a7384da4e9f1c5960c963be9177d55186a.1721773977.git.osandov@fb.com> X-Mailer: git-send-email 2.45.2 In-Reply-To: References: MIME-Version: 1.0 X-Spam-Status: No, score=-11.1 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, GIT_PATCH_0, KAM_LOTSOFHASH, LIKELY_SPAM_BODY, RCVD_IN_DNSWL_NONE, SPF_HELO_NONE, SPF_NONE, TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: elfutils-devel@sourceware.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Elfutils-devel mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: elfutils-devel-bounces~patchwork=sourceware.org@sourceware.org From: Omar Sandoval Whenever a new archive is scanned, check if it is seekable with a little liblzma magic, and populate _r_seekable if so. With this, newly scanned seekable archives will used the optimized extraction path added in the previous commit. Also add a test case using some artificial packages. Signed-off-by: Omar Sandoval --- debuginfod/debuginfod.cxx | 145 +++++++++++++++++- tests/Makefile.am | 13 +- ...pressme-seekable-xz-dbgsym_1.0-1_amd64.deb | Bin 0 -> 6288 bytes ...compressme-seekable-xz_1.0-1.debian.tar.xz | Bin 0 -> 1440 bytes .../compressme-seekable-xz_1.0-1.dsc | 19 +++ .../compressme-seekable-xz_1.0-1_amd64.deb | Bin 0 -> 6208 bytes .../compressme-seekable-xz_1.0.orig.tar.xz | Bin 0 -> 7160 bytes .../compressme-seekable-xz-1.0-1.src.rpm | Bin 0 -> 15880 bytes .../compressme-seekable-xz-1.0-1.x86_64.rpm | Bin 0 -> 31873 bytes ...sme-seekable-xz-debuginfo-1.0-1.x86_64.rpm | Bin 0 -> 21917 bytes ...e-seekable-xz-debugsource-1.0-1.x86_64.rpm | Bin 0 -> 7961 bytes tests/run-debuginfod-seekable.sh | 144 +++++++++++++++++ 12 files changed, 316 insertions(+), 5 deletions(-) create mode 100644 tests/debuginfod-debs/seekable-xz/compressme-seekable-xz-dbgsym_1.0-1_amd64.deb create mode 100644 tests/debuginfod-debs/seekable-xz/compressme-seekable-xz_1.0-1.debian.tar.xz create mode 100644 tests/debuginfod-debs/seekable-xz/compressme-seekable-xz_1.0-1.dsc create mode 100644 tests/debuginfod-debs/seekable-xz/compressme-seekable-xz_1.0-1_amd64.deb create mode 100644 tests/debuginfod-debs/seekable-xz/compressme-seekable-xz_1.0.orig.tar.xz create mode 100644 tests/debuginfod-rpms/seekable-xz/compressme-seekable-xz-1.0-1.src.rpm create mode 100644 tests/debuginfod-rpms/seekable-xz/compressme-seekable-xz-1.0-1.x86_64.rpm create mode 100644 tests/debuginfod-rpms/seekable-xz/compressme-seekable-xz-debuginfo-1.0-1.x86_64.rpm create mode 100644 tests/debuginfod-rpms/seekable-xz/compressme-seekable-xz-debugsource-1.0-1.x86_64.rpm create mode 100755 tests/run-debuginfod-seekable.sh new file mode 100644 index 0000000000000000000000000000000000000000..9b2e48c07a05d48ab5997c9af514ce3aac141549 GIT binary patch literal 1440 zcmV;R1z-C8H+ooF000E$*0e?hz~2ghP$2;p000000001fj0|DmC;tUkT>vr}NNor1 z@tIyK_)om_N=i<6S-%T;fOGB2GRB9q4BH)7u*{O+35*lzS?P;V?>R9m3|5mRC1vlm zpdl4bJBbbDh$n$YctsuncBa4);~^V*qxuz;gg9h)*KSpPVWKeb?g?+GlrlvNrQcgTfUJantdls#`}KO4P-Qg}vo)6p8f-(j5B z$9r@8gxU_UB&?a@ta;Z63hK@27KRfyWzEa4NOzQtw{Wi8 z$z0x+^#qZ_ec39;WI#|n@ud0}_qOEj7UZ`kY~QHZRO<8gTk#HKF0evhS;lLj=c9-490N*$8zT7vVu0pd z%>9W9C;8AZ{vpn91rjn$i$lFJpnIV?Q>E&Im5MaU9M&ob9s#Rte%`1Wj<4kS}vy?~e1TLSIXABEBB zKX<9mRT#-HQt^lH5na8B%Ts$+ELZ*XOOa-{szny%o3>ECz$M)zQQjf3ckvo$?+^L4 z`ZUv0N$Shx2}%oJR7JebGx> z1vL42G?s8&_IwUJPgqn%=E+z{(9BGkjNFZqY`C9(2%%maJ;Za*TgkS;N|S|qvF+Vr0B%O}co(x77PNNIL|V)?0gt+6K5+eC`LFCmcy(ywV_KG>lRBnI za`%y9z)Yd{c8bqYX}qTUX?jw)!)BDx_7hRRpAU8`uN4~fh`r+OJAo8vG9KKTNE{cW zDd{U93d7*#F|Xag)e_sbe_=(Vw#aV?pjHh}6_n`EiLQj#nhkS&j)RJ4aUe@DTNTD` zSqAJ5y(k27IX3c#imik@u<$y<9^Zm(mc{B-_UGI_I(3=*_IZt@zs1Ok(va9>}i6(5dJkmL)dp&Sm63Yk!BBpN9QrvUqU$ga}D1+Cz u+Y_q>#eNQlfB*nA5!+sa^F8$d0s9JoPyhfey;XOy#Ao{g000001X)^04!WuU literal 0 HcmV?d00001 new file mode 100644 index 0000000000000000000000000000000000000000..6a54d20c8b3ede88943856ededcc33cb1b5b1338 GIT binary patch literal 7160 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 literal 0 HcmV?d00001 new file mode 100644 index 0000000000000000000000000000000000000000..923ab96b3082592dc90c48ae2c50f5977ad88811 GIT binary patch literal 31873 zcmeFX2UJx_w=TNKL68h0X(L(LxO0#kBuEsLB#JwdgRl`KD*|F5C`b}XA|L`1MFGiC zK@dS{8^fVIwk<%ECm2S1pt04 zKLG%M698z~0RUh;kf8wp8m_fRGyxAmKv8%+1Ve`5@h}7ii9+H@7y=B1M4<=}2mu905MgitML?sFL?VJj#6buc z42}fBl5r3?6pO}V$V51dguuda7$gCMz+<2&D2_~q;ZYp?~Nf`iK6Zf9N0jhyI~|=pXur{-OV0(r+=5 z{r&yuUoj2fk8u-401zGr06_IuTtm3~K!yka0Q13U{T?Tw#!+Yw;vjPeaTLJ=Jb!>i z{t!oD0RTYk0Dl93->~TcUOb4a_#9wroQ2xI{{db;;NLpH>j(V12YB2jqy-Z1polz06>y`u2dX&fLVXZ)6^c|L%+s@R_g$>{lW}V z2bdaXq6Yv*YMhALKgTaV6ZM)@%z1#R`cX0OFa4N}4)o*u)t;Ghz!&%>&rG$G769mv z{Nl4vLqSXcKu;ZaW&mI@IKW2_Fm+t1eA!?9vr_e<0RVc;ulB6eeyAAxOP*Eu0G~R* ziU(NxpuOP%);VZToflO7bq_E#en!Q5zuF(7#?h$Q@)!RQbsb_9AKLRJ~d89 zmB09l&qmdoiZA`bhg|^x-~a%6{4aetst@#`>cI#AT!{ymbbucpVDbS@KfqMIsPb6{ zm^#k%0KoP8{q_2VxqrXESASifyA=Qcb^xIF{>4A~;y^#@yrl;KA;bZ`c7Un#OB4X; zeGi@|>iyyb0Gz$L`OZUq-k1SEm^z*` zn!umOQvs*~1b_mX2fdJCi`}ID!+-js$XW1^pg@1^M`b$Zj4kI0|si z-oX>(;0bae;an-yN)UzQNpWzs0})AhFFOZUvKvtWASd)|QAo}t0?v~{@&EvU|H-6| zi2)d<00FysfSqyF+VifS?j(W(*?~j^oE==fd;lK|$`(Z(D1cTe_;00u5%v8+rTxYj>4fa1T-2&gu;ke0vv;a;}B3N5(kjr7(4`zL0}OC zBms}cKv8fkf&hV#umlVQ4u``~Fd__thodkA42eueV&Pac76V1Vkt75JibnuwJd}*b zlb}R21cpXKh!`>s217w1I4lAMCm^5%G?7HY5MfX#$#b53=D(Al2K$boPfqd$S@=UkHq7!2n-wsLqbSsEDQz3 z5pftiK)}FBP!bUfCzBvpJRF0Bz)=`HnT&-|yF-wW7zi2(MPgwPC=5l!qoH^riA;bY z2?RI+kAk8BECK>Y<8e?d1d1i$pm-7lMJD555Hf;@C7}rfB!-M5qj6*`gn&RI5jYeQ z4aXwM1Of&{f)n6mfB;3|2oM5+NWvfpL<|{&C6VwX0)_y?5=dwa9ET?2kOU+e4k4gX za4Z~+Mqpt`1O`n)A)!z*8bHEvcq|rz#h{=#92!l+Q>`VTi6|HehQYwdXgC~$f|3b1 zGLis8z@Qii1dYUykT3$21R;`e0G0rQLx~s?4uyom;RrOAL?S}b2pEzCCBpG=BoYmQ z;|W9vo(w0F2^a_x1|h+aWHOO}A|i=s>caGAgn^>bWGos9g~On5Bn(9)!w6&)6oVjQ z$Yd-AML?mEI5HjyMGz4r6b6TYARtf(5(dR%Fc1_1{-63(P&oM?|6ZORC-DxhC;#;d zMFD>yfc+~0NUk}3nd^W4#=|NENaCZTK{eJ`cb4~vj2FUtgP+$~-de931)NPj|yzK9LED}-M!I@-)b0K-k z{m!G-XgS!CJSqRGGr?VRcEkN9?5T^pN;2^vksW;gqsakV&(qRZ4)CBj6J>Rb&fDs$ zo9R;Zhbur75MU^PP(b~5V2=3h7pVZF9-boEvyaK_^MFjwZ^jsY%4mfAfRg#CNgPSYxuMHgtiiSa9SQrNN>!nORTmN@R zRG*Xj83EOoqJ9HF_1&0$?T3H!DXG31)jy;9WK@6W@Gqa?w-*ofyq_BY03HAUs6H3f z&*BFFfa+gSeIcqJMfIJ8005x+LR9}%^kBb9-CK(PdfogbQGI3RU!ODl?|jto8&Svj zcN?iW)lO{jMPi{C z43vN(kzqs_0*}Tb0T>1#5Rh;p5rHOSiBK2{4JTsJI6NFfM&V#kJOM|9zBM>MO3<`q~aCjJ*M227x2qX#$ z!@&U@4uBGg7&MH4g`%-YBo2$jlh9ZM90tYW5CkL!gFz7qSQHY#lK?mlLBL^gC=8hZ z!^0pDG6{(z;PFrx8A3qfF$6RUibr7q0tSHKaTpj1hD8!^Pz)LlBN7QvB!apzN1+Hv z2mu2n6Od4VhyoBqA_Rpb!B9918bv^3$yfpjg@BNt5HtjhgrVRh1QLq`$QS?xMdOhq z90ZObLQyCL6b(nB(P%6gMS!A-BmxGHfuOK|oDF|m002#66DvI<9pFE9OutIb4){Nm z{^L&nX&v;>MPZmfm4+k!yc+kX)i}tXSO2**p7f{Egg@7Wi274$^6%1~_BapHNoNNF z$<>qOc_8~=D*yHH{c%)J0$^hp2uyaSxGCejC~hDynT(@=ohfc$H@qW>K=A~D$s`{F z$(`ch=IRLo+kwD#9wdUB2N7&XAb>r+9Vi5Qk|zl4?%+-Wfh};(&LFUbs~eblJ!hPQ zE7;q^&D9RlaC3F@@&tRidONrh!4&E@XVk&{ zr3S$n=V|}DG7fI8ATXIg{?FLe4eaLPKmn6Ia4saUyPJb6h2#MOyZm17Ou~`>GZP@V zJGcQpz5wJL>>%m^{xu}v-y$JkJ6CF)iAvP>^xsMUr|FMt&;Qf<`uiID- zi6{^H?bf(^kf=9^IvG)jlg`A0G5^&Qb&Cg+2We2(B|WVRp7uECMP)ed@2w8}Zx@o;c=ClP`F+jig}e{b#jx3>fL_g2<_cRL7}zqgwIyW2s= z{JkB-zq=i9#NXR5{=3@&#s9s%^uN0uc+%h77yrB4K}7w%z4*Vo9Wcz_^Su7b4x)p{ zZ(F--0HAKBsiy~`kbI~IZ3jg4;igvR@Pz1XAC8rf{q;#mSnH6mO6X$z$H`L~WjU>>hQl`6 zqk-vA8clI^tE-7?h*5!6{ELF^qDH++zaQKm)$~>+tOI&m) zcH?c$cnf+mt5~qhNRHXZ>Lh0s@aB}kd}WD#zPE}}@II*XU6?4}EVy5~VnjbXUcFtCoI3D&mN#CXd4xK>Zc6PwRF!>70klEQm$gS5l^6hpG~# zEFBw+@M+@@E=P!8c}-Sg?t1dZIKII6x6TYp!? z`m<~OmWfrhZ@EkOqd`Ii-w&q~4_mI3AB|nnoq9OenZp>o<#(S@_%hQY&Iqsww{D{1?#ctF5| zZVU&OW>JTi&(I^-g3j&Jjn(KI$GdQdCquf2WP0ZxCd}~{`yFlVr1X_O`u@Rl=t<3Co%pK0K4vwg zx2{_Qk0l``J1MM-<`sd;iW2S^pN`TYaApM|W!h|H_pG{6os3%syRd^sn&3Op(|RWd zVW(8pN=w=#dbKCZC;fXh;J2hG@S|bo0hCJhxAr5UCo0sx=oy?o+leT97f%YFR=3XD zSpi!?f7B5HXD{(LeZiFW{6dbWZGuT5rD}3eu1p^Wt64jWI&9z>qj8_E?NQ7#-In~l zOQ5LZm@L&#TSm=NMq4(oQZ2Fm2_+Ais`j@}dx#ox=4olG9lQ3%H1{>OReNKQZ{;D- zRn%Tb$&GxKe^^`T%c+fYw4DN6IQ!V6Ag-5AEFqG$HTCZz1bGCm69t}6KR2-x54HVa zcWtUsET!y+&bLlie!VV7?P7grkb;?v_N(-}vcp*>E6F7lF`lF!?yS;Lsr{)w_fGq> zU6%QAGu72u=~Q^n#NQ62{LDiLu%R0UuKQi&BmH_^V1+0D8l7q5>z z$=|*~*VR}YO8X%?I9h_3)=XB`Xu0!LxWU$sK4Mmt<#0-Bm0pfkjh!mu^VNvf%aNxZ zT))a~a&%(4%rY_S&9_F9R{L}MV4boY_>NTYp-mJ+LsOK6(d)*mW(m?$7)soW% z?4#;kitNgRhF8qMihjOIL$FIHD-Wf^NhUl*muUYQuJize5JrZ4c(u-jCPj`6T;VGu|5YBii$0*-e za;Q$DPEs~s{#QUaSi!cIDl;D>f-Va3#8fBc@Leq!5s{G+tpU`2Z$< zZ|p-+;Qm2=us)YOS*_Q4b3o!*KN9$!Bo1NWqrB&LJJxt61N2CC4)yQ zj(^)a(f?ej*j(^Xxav0?;}HvfIlBjSi-JZ4h=RxW;)5iT_817rTeECwpC1@ApI0$; zT0ZOgWIgfj$Ncj?5zBs@!3wt;O?m}-F)Sk-!5?>8j$sQpORh{ezc^d-**NE@_$G5j zK}y2myk|v9;W(R0OEK{_#_CIA8P-LuFTm7KeLNoAfHnpsT(T*UUA- zozy!#$U=?B7@WD-ZWnEJC$4PVykWFfaMRIw^W!yb7cj&{*hc_tew}f1-$EY4L`*zXsJm*u(pc}V)Acq$c zTpdd!O=Nv*wfiC!1+-w{!ncE6)sf^rS0Cj0PgVZmx3#~aruO-pMBmK*RNSY(WkEX~ z4tdDm9=i?UH^(sbi-}2k>Q|IZ-^5(q&Ky^-z4=l#Xz;wTSp)d`Z>tJ8w%~C~kk}OuV%%YCn zd)a~jw$V{vF1|3vII84au)q5C@f#=`U3HDRD?W5 zNXPGu{vGid-!60%1#?b$Cl{81`Ol;<&v0~Zo4tOIG}!$3DB}>hGv~#L5h>(_)~#0( z7ahfYB6|et!zB4YQmtRy9ZnwG%O<8K))ejB!AXWA13L@9eonVjqoY7O4L>&U57oIT zr2iQ7l0<9UL?-rYq*#A=?!lC*4mQUMz;}N(h;RyugP<>2_@4e2F-G2Z(vsn zUP>vRNO$KW%F-NpD`x5=;eW@U52;y8_mGvv)*Z@#^Y(&Xs!X2Y@3`~Wfc^cMz6`sU z&CYkXW^=?Z$>iPJ8h@UlBC~8L^bR%TNLjkl7)5^+%l7n2iYGsLEC6I1&{GnPe;PsAb1 z`zh!8=D1N+z2r6e#O~<@LA|l#ZS}O-B67SeU1Ng__qc9%G$}=05`Kr9@N8*967Ll;xC3P{JH+t0AM*H|@=rj0|+G)vZ%^S@(+x=CB>7A-<3KDV*Z^nx# zYtX3)o_{sYH|~0Pu*Q(b!d>J8l;KD0$SSjCRL-fNZ#eJPr7C=-jD3jX6@2-mZM)4x zHc;XM{j)ogC;X%^vjtCdKa2Y@CcIf@8({GE-1f~&dg(sU^15f`nifWzuz;&s@H#6n zo$i!!TRhd(tuEu_P3yB;10Rm?UhX`E5%CtwPS8);${mSreKCLR`NxRJppWcAssY~5 z_#}%rxDzgwI<%XP6!7XAyocGm+V|?*`}U{7xetMu`|5mhHXzcBD`x; zVR;enXKd%+t`dvuDe}V4JWg9T{8m!uf!8;iJAGf|5h0ZmV<&kzXQ>N|_|(1J9{q>mhOaf)wmcI9Gg zy3N%d=IXM=acSC2r~XGH_N^ZgT{#fLRdg1VA-dOq$JH_Q4waVcpL>^A^a&7NcCj%M*WAQUb%sZ%{KXS^@nU8RT8YKA_4fA-_vo|WVq zV?L#d4*}qK!bIxYXL9e@*n;wIn9z}!JNINoBTwudcZgAqXZ-BN%{?VV%Rn~t>e!Dh zy2vpodPM&X>zc9&{iIlt%m8-Urgaqr2!8vb1M@q>DV38{X~lBiplYtWAN;jv#>X`Ba!!?bwY)pID8F0BEcmF(CwoA)?Un4r z*Ll+!Nyynz=3DU#eIe09Kh^I+%#8hI&7zC1@j21Toaz&8Rijwl3Tf78sXpTk=QZSZ zjBtJiKChBcb=@grWqJ?MtA=tdYUXbErFIbdFb^u`-vaKSu_iF1b)w$VXcfhk4m4 zK%4w^=8>8Avv-!S_D?j^71RVj^j%TmHN#(hZuNe@o{k;tJ7d-Qp? z4#j@6OQ^b|jI_6u)(+rcO;CBhF?RUI`(_R0$via;(K8uUoM+n{BE47``!g3HE;1%+ z;h(YCtCh{iF50U3ZFO8oj$JE$x|`?7otYzIDiCC_jIjP9v)!5A zTfGG6IETcGb&386{!ooW%^PA>CRZAzRUw915i;~j%D(Z4WTj)zjBSiwvtL!q_EK)* z3%fYQZcGMJ;-BoEIkJ4ukT|Eb96fygwEe(2yFn+7NAF+uKTcd=uw7XgGf@~DNxM%P zIjMg6`E$D^8>=2y`h{mxPtMIo2^RdgAFW<8g19 zUQvccrnNz3GhL)`RPyFvsYbhI)=nC3r$TzLb2*NyoT;+*gto4Ici(FLkn?(yzwo6G z^68FV@?!~N^WPM!`$uEDngmukE`v*N^?Mndc#4L6yW5B-1}u!io00aZ?fWUgwjbFq z4lHE6gL%1`Z)1*p{yOvSqQ^$*sSLR(jqQD0zL2@zMv1#6X3o$G@#|GQWKt@O%zBM}9y;1X2Qll`5~DNrIxl>U&t{AJBQE*$A=f_D(>80` zY>v?VfFV*ids?%J%9BIpO((n9q6(zTj-QUaPyTW>aTioSKw_*~ZV8JHhZuBpcV)dE zmAow{B+gU5ol?c&R(X->VXb*_UQmoxQ!ED|kgwnps2~uyWFA~b02h$A9JB#+k#!t3`_vKLuX{fL(h8Hh)nlx=(e|6OB zGm5toX$4CcI(I~OH|s>58R-EpCDCJ=on{18`th8)?fTr}QVrW3Q^qxq!-|^6s&`M> zoH07f%c$6_`%WJ_^y-47@#mbmQqv?RjgRFWk-;b03yZ7AgT#-%dHXEnN>s6Z)Opf^ z$Ha*)0<1rU^Xy24fK}7|*~yTCXHTCyXp0#z)Kg4n$GUtzzZ(D(`v$XuH8Z_cBNX{| zbhoSp=u?>L++Ke3>dM2|b5fPi3H!bA zk>u{{i;$7b-X9AEe)y(jnvS%LhwIg*otPgt=#B;S)}$BH>p@f^*E47dJ&As$7h>YF zF|K)w3T5IY+ks!*Tx9gab%)%zQBdtXi&*1=0MBohVL@A&-#5cHMP*~_tBzgWILr`~ zE3#gY;Jh#=dxk|0>qLJ4_#@i4Lc}&vGCgt<-cK|9ltq``#Ezrs4Skd%KUoMdRQQ<8WsAmL0ia zpR89M%NapItpkXufC@k3kS~@)8xHsFn6@VA^2PX80=DvR8Pq>KH0l_<#gkGnx%u%F zP-a?yMd2$NW8opU`ye9>Ny8dWVc7+Ubls}d#%dm}V5SxFSMi(q#Giioj1nsPbb}`7>ucMvL_pBOaGAf) zekS*1Tb4-rONWi}Q2EO8{_vGku)J!z^=`NDI|*TU+1wYcgTg(}zI$*^)N?JA^3pHO zHE3)W@4EYbXZTi3sf>L3(}FDHb|c`YG)1s~rIfd;G+M&s`8!+h(6d8XND zRI$*pD=tAyV%25RPbe7dQp(-Vk!w1myuyW3L5 zXBnabz-+6GGhcfPyrOqrU}Po6^VW8G)!0e%$#HX>YwquZ-$u|dxFAb*;};rsS82j7 zqsq3?qWZhMgOmCG$HZW!ubE)C=D7T3c3jXTuw+tY#v|0*9C`z>d8Von>=4)R%Ilq& z8$~Xcwu-jbbhr}TIV0KAOMyzkCrvqWeAiN7L8*tw-th zEg}z?RURap&Wgys)>jNm8a{(LH5nIJ1OG_io{Aik*^}h0p5^|SdFSODNmc1qCrSF{ zr&{=%j<@zsSh%#lPR|dlyN3Ll2g@ogccb|9#EL++)!2S~Ev^;SkkC)+h`@zY*zh+4 ztfr-)3z@G9#4`q>Z*^r^QaKMY{|UUl^`i##QG z`oi>i$eFW@LIFv6Sz01oJ?hDy83-{kR_RQ|gG0K@KZ+krF!C>9!aSTl!IpgWS_i8X zD#SFhuV)p|wenh|+|$@;K^ZA!xzRtEeW-pfLgJ@a(=BIh?Lze-gL5F-ipR{LAs-Dx zGsL@Bl5rJRbT)Tp_>3LfE45>B$-7EFA{f-G+a^EpSQn&JDBblh9?hPXSp+VLoofvY zTVX3blX(014D24EII(>gJ1r7k#~n{s*@OV1sVnN_u``P1B}Oq@S5D?_4-ck?o`)MNqU&i+>V z@!(}wkJOLSi)xBI<~85n>Ahw{vpuO}DC>?`Xw*Bzg@vWb4TejYe4dNa)tvr192X}f zoG!dX<|n3U>=XS)6!@L*dpLW~oy3WGB~c8x5!Ri%v?g*rA2^pWyxB7^0?7ilqOY^{ zTIcycG=rCxyWjo@{YY~iC;Jl=7NMo_YN^itZ9Dh%Rk^17FQO`2laoRySr{er^`&;1 zGki@s*dCTWd89=VGgnzFgMg;n->4~>|vU#{1cY%GaNb&rNNzAY?%yTjG5U)o<2 zUg7?--}-WbhKk&nQIfnu+Hx-I*-~+an0ayXxz-L&u2lzcO5TiNurxX4bZ>LuJDN?I zV2uxNVtRE6ia)iwd})##jLlB3dPyu@T3}{oDS9Pl6QrxMpw;xu^9t|287}|1$jQvt z^<|n*Iz;ECC)6QRDqr+276Us=Gy>@HzGa%EM`xROQbuhzGvPEs8G5|nu*%49=lgeu zwC8u!0N^K0tE?A_>9nfLrP%QXqoX4n0?Wf)=YfhEsgZLs?>OmdKU$A{KlTaQnHxpZRiMM@5&<1B*LbabI>OGmL3#aZ2M8=lN$ z{~Yvm@~#Az*_hRlCa>EQ-kasIL2Qn-BNRR2#GH*;F@67r7F)2&NZ>QgICmD>I~NLk z;CHVuSSfnl|H*WTV>m8nb8q@Z4tg#xsb=TP`%Q>IS$bDRimcAe7xTWUJ$Zfk;xqC5 zS0e*Te|}nw^-y}fnPuc8S^p?o#L?){7N0HO5Yy-DBG>K&B46_}-mQC7foX_+PlM$D zda71+;;{W({-=9x^kV(sh0%~K2#d~l;S0e$=;lTN<&x0{*HOH6rrpr`vvMytBe2qo z;8WW!E$Aatb-NDst(PM%{!BBU#N%%$?HtZF9o|zXeGM&1d~roXFR|ZzG%q5(>O=ZG zSvd7%C5Ntri2GOBZHeHtT=6tPF{u=`^0B?6Li*L54<5bUThVBh)#O&n?=8M7IIDfV zIQRo+482ItjV-&ACvF8@zQfuq|CamN)7G$35HkLhJ`mDeF%^Bi``nO<*rQ#ue1X}U zubXc;e2DfcnDR9l9(V5^7;0q)JMWpTrRVGIifdlxYsPn*mQmhvROPdtFETija7Uhe z^aa6pH(7p<-(=HnLz$`Qnx$Uo)0p<{`5Y^i@uG@-lef-7_MYovUT7P>L%npp%{o7# z$j{aW!)NKoCb>B$7T_^k(G%>aDWpR0B5w40?h@ni4NU3~jl1_%^`yuCO8P&AIP}XZ zUaS>>MtF3O`tfKVTP_?-3g8L0VM;nVwHz~Gmp^nnfaskzqTcXLN*QL*H2p03a1wbe zz?{AI_8N>{>*x+#IjV9WLgznynUFJ;#(M7i;*qc?QrDFfjV+iNwLa(URqaV6M@2QU zd`emi2hmGQ`kp+!OveX1?p75%;x+$2rCD@Ad|G4p41PH z7AU$5ms<~h6{)Lz1JcL(b*ZYi-}b%c?xg;ZDs84{JGO}eS?ay;ezMd2)9#Pp1(>X? z8zYC8x=i6n?+?Li$39{hho8E8&(y8*8mBtgT#6m(_bRI1uiop}s(a?A8PxjF0Fv3L zijD~QGGsHj)3UvqtJmY~>d=?k6?a|D-w5|))hqL=_iS#q_BcJ4s%|@9P@^3^Z+*RS z<~dmDqff>OStVj7ZTf02&R)fv94-Hur)L7>`mAQ@_~TB_7VW>?0oMwWJShT>d>K z@hwh%A~y~x517{p7VkZbeV1E*D{g@BXnaRAJMu*Bix71fHqMi9DC^>7G0o!1m^_Ag zgOdDHA|Nmw?X1Ijk1(=9k2hjr*G-cBo(6BYpv32g^+H&10C!+{XjWNg=^UC>NVjCQ zN9pFVnS9}Z4XJIdTK&^!n^znAUJ+S-2GFnj(ev2dW7c3Cbt>Vg@h*pF4bQLr=y-s9 zt!ORb%k9~}!nsMwh*Iy-mxqm4i_Vmn2!Ksw*gYVbe2_;Gj>Cx zDkk8qm+M{L!NN&iaeC}BQn@h8L_^8AiKX^@;|uke6N+-5jhf#)35{}Gch}btmUhQE z?jHRy@pN+h9^@0N+!3S7qA}e!w9+g~r8E!gc&-t?njZdq>T$t~`sPy=@_XkW_**AI z+o8JHc_n`!D|z9jcgE!>!z}x=0nQf|*RG1bd@XpWdewWTnT3rmEE^xNv8j96{>COd z^F+(*gxlwv5yL{rjQc!icQY?9J4{_0K4tE3U)w;M{m85Pb^NOjk3*yvD7Wbw@ZX|I zMV@I*wTi%iu6SvfUGvf{SxQ5}WNR;pg|T6@(Z_MwE93#(00`esvaWn{@@M?X7WG>( zb#uF)!S~oqUwl1z?~GN&a!~f+)+;8aSMx*(ja8~`dl`vm+Xh1951~?Qo+*z#^oM44 z-A+Dl#XoI)vNr0)o}Ob!T1#2pK&ob$RMtjdnr_Am?grH!%R#R0wiqLWK$M33(Q(BG zy!+WF2OE;)v-BGwoxC7FK^B2)c-9+k>W6jXvk&7xm_|ye%}HOqr)tq6TI*z?n7Up)8COrf9zjmR)J?3j(&yK~>% z7DYIcX#b_;YEY}(lh?P6MQ=u#>033_L+q^Ps&`^4xp*O*tGhhz!OlLkA5<}(X53Ko zN4SDD%_lb#F3^7-+hv+d1v8|*9<@NFxVZdA(LS;pFG8bACrFt2-wD7v$;mw_s)=vU6^3wZS zdN-~qH$L>algNDTHT)WI#Qi0mjg2@<`R%~q6H^&(_NOWy@O7U^h%d^#&&)%&N?XBH z&&pZgc@d50m6>a=F=utq9=oxurZZ16F_5E5QsktX>9w z)}A^BRh8MCQDy#2TY88%8Fz;*?tq$awX@rO?Y2p>d zV#}1FXQc{$%#Dd6(@%}&IRlO~kV$x0b$_ZKn^EJO{{^-)r0;QGia6uS8i|8Srh-L< zZYOSG<5WEB2PVDv`Bq<#IW!+ziGrvxa$L$~n!d%@3G7?Utgs#49=`gnbh*$bTm9BG z)xz2h?QD}YuhqPxsgARJhjVIyj^Sj+g|3~$87$mV^0Zpy%iGYgSd?`P$S~YED71w*+Fyh*>BIrHC*6ZVg zLxqmzyO$pwZbyxG+SuiscC_>_J#GE))V=uzkr-U=nMitAGWhK~dGSw$rr-UnF9&gv z$Fw3-qibbrO4OC))=axQ0HJMZF3=1V z=Z#3`o+2cz?l{Hjf{gF$Xb8#qXm{ey38~M?k$(0@D}GWBVyf0Zqxc|@G`I4SGB!5P z2nwFr4%h#Yc+upV)oX7$&Acfu(EG7m{HR#VHXmksVtY1z_q9@@^KGp!Ps8G}4hi0N zv9xN>Z%BeKkByK+ndu+{Q>_vDPexc1cJeDo$1!d1esXeGsx{n$Nwbr}^#O)!D6+Yv zd*bK_V;=Kd;^%jYG#zMwaZuO(T6}XGxIXNY*3>$qBg@7+ow{a^l`;+`1J)ATaXS0) zl{2R7PM4p1s89N~(-(De9T9J(Z!7NQJ-c6~^ZdK7E@@wAz9j=k3~98xF(_f;lKI@Z zCHkI3Ptu9S;vF+l{q-%&?qmlC)t6acX%KNy=WH3OF8hXR$q$!njyF|{J}Thwc&b+5 z&8gh8i{Eb+e}3be)6mXL|EG}h5N-;A2IGBMM6=dLWpc^16uzbr#oKajLJfa<1Z84A zNXr@UI9kffpsOM|Z)I;#9NZs$C;EcAnripjeY*$^rqbcWhEp zO&Fe0g5P{)sic@{0)M*#C`4%IieC62pgCRrm3wrRP3Dk^*Il{uR^3GX*T-V+eq262 z_I#1wT~`^LK*K!Lb;VQQXK3m0VOy^PC*d>dj`S27w=}{x-PsDoc5WORnQlb9-X-vhTCoE|myoOe&Z01a?mq<-W4Z&}_mek`|zw zV*(9DN181|&b-p{T1~;M$G0W4uB=r`SV_D%%vdK)cxQX!YidJ^6$aFR`WeUL`U$gICQr}=X^wRsz>ygSnC%aUvfbb?!Lu8TlX!Ii;ZqLB%@Q=;D9p*YiW+~8~ z@|+dQ=n?VQ$6eRd8T@*0IUP5~Dq7>TEWP7GlVoUxZA#DhiPq*v27w(#Xrz!nAWj_J z`RYMs8wXYqE?Jm|FB7VsL)QO&t5a~k3b z+6bR$ zh`aPrHBtpFr!?D@b3-y%i{GXnw^kiY{BC`}_#UPg-R^C{g&zv8eLl@9&e(dcF1>#L zvSjtN)8cd0u2+ZhRoa*$w7Odlls@)s_N}@|>d_R5NFm~)a?Y-v> z+$@#hUMHRMnjYEm6iNIPXe#m=_R}qvQ%$I3r`(Rymc6h0oeb0I7CMjUt)on8+8txC zIP(E}Lxbx}O?`_gEi^wT;Mwt=)(AQ+o{eKC?`ep*9J}l1OY1&yc>6_i*J()wZ6B8) zGrM)nxg%e>_uk5JCS8=`j=9yW^*Ek2`e(**;e-c$>SZ@h)2(rBuwCK%v6sSMrN`Cf zo-F6u#U}OcGe^WE2hZ&|HbdWP?TegoPEN5i+*eXx-nj$W{&*)9ycY+vaP?D=Ys}yD ztjqwXZs45~kB{-irVbbM`Ro~3G?cMFHjk`6C$=5?YNz0)MDE=4m4W@nbC%&ZZneK| z@hbPYNbo_7bEmF{2G?#X{%nRLIHp@S8-fdmS^I2v7yPom-N>}H-3x?k*K&RA9tsRD zmJ1Y+c4WAB_QXwM%qz-u4h@c{m^TWgAAmTJ)DzX-jSqPG9J^D)iI-@+ONVA8qJNy$ zD{M<~3jQd;4!i%&XsER9%$3+)4O>T^JJaVQ5g|>w`pF;oXTuz6s~eY{SZt22e|+oQ za;I0UZK?x^7zkB2G{oHAa zvz4V2%FE!LOw^Z$I(IMGa-99dE5%}XY)g%52_Zl}P}*$oLL784KY zmas!N=}rS?rfVBt?P}8^2V^Rf$Ml0cz!8QV9D@awMDN0k-8W3lO3#=pk1`^a4BptA zR6U;B5n*3S(}G%@a=zGkbUi8!F9Gw#me-CaT=<&zDD4+y2U2OV}2#cSZOsC6)w@@HF_V#*A7nB?P)#BjQU2+rDlOCRasX%WTWf zg2KN~-cQfA&^-C3P06rn=*&dYbqX(+hRERyr=o6(S0zWuwm0tj$?%zJQ@SFU<#KCk zEtf_ZvbqJhZLH7e-BOKb6LWHVa_8AMzrB*~%bqcTz5TgLxI22I0S}JG)2^hv87;iW zWZfppv9}>Q|i=9R05XhTB70JqVIA7{9cDOr1`sxj>{0Zw9Y+FA$@+xND z^STRmnCP0!H(iyS<91?ZJx$n{rw{J^IuKBSkr<*|m^m?-NatHSA`Sz$>X@~eyb`eE zeiU#c>xnSuYjp+ z^Hq=euCqmw3Be}OLUHl0&knvRPOF{yvA&*WCy{j3Pbx;uDXv2Eta;MpKg1xDS$&~}b& zBiifQha?AHxZoKc@eYldi+JAtsdM#w&$%^+T5TxzbZ@~o`RLpr^A3Y-6NUNCK>KzG z{myO&T9r4~^GtqdO6_Cg{Y&?}Z3{r|;aT=9ih>a?9u}`#?L!yG*mLg)dGBViak8s9 zFx?1^OAf+cYtDU+XzAP9KhB#rLEnETPejz}4WUFNE3VZoeO>hv4AxqHZxA=nTQZm% z+^dUhgLLuP31rqZ1@=5`-c(Ahb**H2s~@%z7_%d;tu$popW{Znw?Cy@SX)Uxgn=mH zIUjb}oZic6PYjB9zZNYX&%*~pQUSk?B%a| zFrJ@pm!r@QiUV>nx$j~+=wj$nCAPc7?FJ)2{bGl59(lJTQ*K0deP5^PY)->b4gYMK2PL7+Cw~_3>DkVqLY~unP}tx)?^w>% z`XhWqE!;@@xNl5oF8xM1?af5(&fBY$+gUub&BZLW^W%=b1*|-B?^P7qg=5!4p0g@j z$S*AzqeN7lD3k8CE-a-%-QT4wTb|MtoGFn zU$%#F?`2W>U+Em1l(yLS$9aV!U0%Ixy4?o4(%b%Zy--S~u@9Si!Bjdh*fk=Re{Dm~ zBzHk7K!7YEEA#!L>T#ULn3qaTw~S9hn7D`YyDstmgd4`WRbflL+7X}GPLRGA8Ftc; zK3!?Y$1u<@V6n}~T_V;+$|}aE?@4@8PqJc_+j0K-?7FZ@=fG_8(JuAm>-uH2w?vb7 zE@ksP&VJ+u`5yrl7V7EYk6IUKe;#U*8O{gl&zX8Y_XhcvjilpaU_2c7uJ( zveCe}t~`MX!X(duj|DcBiZgb3N%|818?n5RVn|!1=hb5jJn(BqI&*g14J^}U z%!$d;v%*iwD_KqXDf2%|W>R8Fe1@mUtF1}<>CV{kKgB%H?%oUpy;-f09%~1BbZ&Ri zqC}eftkEy2edR&ZGW-DL&BfjEk!v#nYfnMKS^DxV3cu8@jY(f|{Ys`pJJq!`8{bjn z1v~YIZ3|8qC1i-+pv)ekA$;_0?sAsM45h1;xns5;ckl`w;r<7&ucEAeB_#G1wA0rX zsr&kD=LCkd|DOgj?%5)4(YU|huSps?N=e_y)}T*=`F@VFv2L{8KvOLo1C_(sb>-LQ z8DrHm(T|V#GQKI-L@s1N`Wz7AR~4!B9>9v&Pk}xWyjw%7Swn#lFk0TR-*8tZ$|a&C z8w)MtsM_Vpd1|(gJN?wcBD&Nf9!)1&k(V}OAllr_sigbJ0*+-_RzjQOF6>2()Lccv z0Q2qE6JmHSCLA{g&UNh7Q>B-iQqP;-Uh(23FEpdMBzQ>=x_vz2yZRSCvvETA7BMMR4?PAZ>Kg@(Njv<%sy&D}0@K{kg@x5*uz*9@|K`1L{o zBs(+UHYME*ES42_YcTXvg;*&ic^>f9^eMTt)mH$twE@PQinpg5L z(Aal$bzMoI+=NB`8JbzKs_Cl5XRDZnE&nI-Tf~dJai5P)r(WgX;_XkHK_CgDkH(Y; z#-2mO_wWJ39Oy-@t$x)YgvLfhnFLxTLi&I^Gck*_MjRYYyd%n6rp8OWmcyH;(I=w6 zRDppp(bnIK#EH#*&zZUDGfK+|YTR!hKvm^g#oRiOIcV8GQr}zM3bZ zrZ0OHm9-zvg8nYY#Q9W=xcq{>{K**n<=*kDjWYfw^^~f=&4VRO)CjiBq2dd$a+)DM zusm>Z5zwE70ge8ha@gOS;OR7qhh#nBHw`}}NL-L=I6?TNs6CB9i`aWM3k3rVKk9G9 zo=xQhG4(eS?p5=WgSev*dgWjup5yWxuoSCR1<0n~+D-Npv3Q&q9@B(~^EfR&Xa-l$ zWw7zp-V|RKY13dNU5lrNfbl|R<>jw<4q#M$>drLHB^d8A=e)Q(rYTU2xPTKRe}Ng0 zbTLT+2$YcY1};2-S^*5a5o|eT=qDg2UYceYuvdNSQ2=k6O;$5FWSy>8hqq9V__Ms_jzoZ*Nf%6yP zQje@@AI1GaSn2jhj@^3B~3wuvEE3e-@$OYq6( zi>woU)pQ-24h?x@Jtd#8tdT3^%+Cqu(Hq3chP*!E*9dm7W}5(Ol#QY3+QF78ArWrZ zhj6|;|l!@$7DInyO2mjl6QP7+YF z&exfWJTOHlboN=JOjTpr@8ej|Sx-&1Cy?nG-Je9x=R7!A5z=BPYqEngxEUZdn(Nzx z-yOTP*#y@xRiOWl<)1^wLIOTUFM7N6s{>A7^CbW!2`uYNG03G%1uwOq(tzGOD=kUW zoi$6k8J$1<_@;0(2XC#s-P4i%E=t$r*~VF) zkndRVsDYrPBV!=70(E(7#t;M$ES=*mJ*QcmG(hgc%PoA)2hu_!rVWU5VKz${7Uk}D?uxMpx{574c3ubSpXvY0#V(YPgRL36t z13~w7Gd^E(r0Jp&Qjf|gDJYP=F#iR_@9fj!m`&1fK2Z-sBFvM88vL^P6}-J3WbG_p zzrS>+3@6)L(Tx|{=9llPDFNH%yR4kq=o?-Xkx$4WY*VB6cpsglhn)D9CorteM7xT# z+7271u(hC+T^=?#zcjH=460TELCvkC3sHz4z_k0~gunOX#BY#E3ypV8pb%0NIpige z73cF4|H7L3=d72QiMe4CXRgI>tcc0K#5ig7!ibWxpiF6Wm^jk3!h9;m=O2kj5P1$K z#mUZ4hPDVyz+FhMa=4JeCKPdZQ2uAdJF?Gy1~P&yE*ICW`RoDucDQhI1r%%(Dw5Z# zdAdsdeXoucMsR{&I>Of&go+>j>fd+*0SKk*N&=+5$lwZ)w={*&TB7kmC@ARRYz6-+ zV@U|L$k^;n1JgIuzc^gnHVUqtTAxG=MW4m*4l_VpXCTiA_^sV04GfUEyJl$Sl7 z*Byfo2xU5UO?ro|%&~liNV>McPGA@##kDAeG+V)z>_1jEu=BmsC@fL|4=uPiGRo|fAeMAUh9B?j=gw}M zQeeK3hxkq=dy}eWayxtl*G~!p^AJv2+gnbJF5rHe=QiXx_?d;@pm zlH526N^hZOD53~79d1=OxM zTjCv;=#0Z_%859fWuS_Nt_&P1uJhA}9-K#tX&&)Y=Brfb=xP6YG_}2fG02O%$0JH0 z`-trBSRIpWulfz|q2e0dOfBdRO1;~wy1<3gEDhUwPssy`xLCJeZKIJ%IdkPy8wwgRkV@g=`>G^99pz$k9mtCG{+VWPE_Cu!4kgV0;ub4AtF1FwG+W(K3=gcbm!SEHe2>YGJ`kh+CFEzjxfp(F-l8df+! zeCc0d?Sj7fm<$!Q^Zd=HAYOXV&a)dxxAjD7(slNv$QIyl8W!CzH?oMZ{Tz{%{-kUbF@Zz*qvw|gIs0mLcyHWwYV{ccP|HkZ^ zhzHEtSHJeS8b@DasRyHgCrEX>);`nG&Lob{dpJm70NkFdyfc-)!?X!zi>64YjttRH zo6onELHn<;n-uTc8?_mU-#ozcd}wV!&?$@PL{_34Ct#eKp!9tvxx3_01PB)G@yN*{ zeq5sdk~s9?i_^#9`rY!#^R&m7>p^m38Kya#14M!d2kgE64hn@CJUG=b6wm$)5eJ5*su6+9jbMBo8LyJep{soyN_5B{fw8=pDuQq- z2i5-Tr~^3~o<3Rws8bu>MFdXh5Z2JZ;saF9!E@>KO{sQ<(v`X1H7G6ix5|>XVb5{o zWkWE!p*krZLU>HWhBHSHHy{7?6%)v z_*jqfj-fZ!a=;qfZ&_PMo3oFJHH^Taeaw={&rNCh+*tiwD9F+j6=9ULUVJ)Fo2aj9 zJ3p|KrDh~vwBV=o$0B{jmb(q<9Uh0ZaK%@ZOFlMpYV754pg#VP<-p$OdYm_@Yynzg zYUB_yP74wQh|^o1(a=(shQ5*-CTCXv2p>wC&_(nrHk_WINI{a?~ihy>!(D72smAx%sJnM z_G@DZ8gneS3EWRCZxDEql~(XJ5=Sb|Srbv+01~--YON)BmQiy8-JB`NP*Mj zkC)!8X99Da59;0NAH(VS^@S^*$1yzg7A3Rb6%^WCkEhTdSV-j2h`AfG1;Knk0IgyC zf<8&TT7;)X>N#cy?9qIXdUSnjqTuun=t7Au9$gXXlxQwp{Y?)L5x=tR=cd1B$mN5l zqb#qiJKAzCLaQpr91!$Yze}#NUHPY@dfGkHM8<`P?FKmB&dnZNb(dvQ^2%`LIdbl? zRRRe|()lxb0-O2^Mqr_yIYnxXQ-t2WSEovKyn z70fc5^gSS&GeSP{TyV8IZjQ6Z%a-Ug?ZRIC>Y5lswE`h}hI7<7Ok8r~6I~o*G4u9Y z=-lpJchVXkU$R!z+Bj|jm)z9ofic;|^jh(@j*qYtE79>Z=o4L~xn1KBwP>1U38D_o z@UF7H$8YUvZbxqZ1Vdw&QwKelxfcK#_=IX)oj_*jx~ZU*c0C0UcVTCNFwqW#AELa8 z)MFc}8UeQJRU= z2v$B6N?+MKu=kZ#o%GUK6mEZPFN&coHblRkF}l(}vf$b{oqfcef?r>@h8iVLq_A6E zLyG5qgAF|>4;nAU8;OZkB@JGaNS`0_wD3~`iY#@;fOlWlI)J$m7!4mW=|FGA^9%w& zTvu1}*b3C{9Ak0r1jIe4PeCSK)#4-!InGt5UD(gh1PsNYlc?Jp??_!MRIZ|}@DZMD z;evQEl{V4bF9%L*K~I2amZz^I-t+=h1xef3x ztzZK-C=ie&C6m>?u#PDB;KRJY?wC`LtkOp%oYVo}k3#c~bn2qY+G`IasZQsWtS zMwcI5HEXAcG(9W4)w$@>p%iUN1;dl?P5S1iVv=WmmPO(^DoB}@Y-#;j7HWCYbsIwV zqmDE2g{Lb>3SmPB=Ts^m`>ZC=nccaPL*;k8!e6h6fwZm;d;WM;Xz)B-$Vx1p`P|My z=CxT~l<&8#Jpya3`+D_Ma(4IseCJG( z-~D~}yWjoh-kF@=ZQb-;Q#=3wm;@ylQn985G1M@wK;8g`)wR%vyuxJ$C^BMgb80y&eG2697{;0Dy)1m;f-PP@GP(0wDey0Du_) z5TLll=nq0h|M)h_FQEJ-%GU$Xli}^Gm&4eF9HNs)Ho@((vonXHh>AvY`XoPaZ(c4V_T-8RvQ zqZ8;>*Ou^9U46nwM^irGcER-r8URQj`Nk|}F^gHuVivQQ#Vlqqi&@NK7PFW|HIYap z@|2-zfLnDF7XX$<0KjNN*RVW8eS83bBwD&oorLHpaa0GnKy{QM6n}e%u0nK)E&zZB zDBnu)EQ-G~bQM!iiiyr*0l<_>@ny<06#q#1-%{Le#4}}5+(G$q6#tvzNd`6(jmHdt zX{s>?Gnq>~049=88~|pLE5UOpPNx2(4utP&)Wbw{B7*OscnifuXCm>pQQU*_dnqRK zOaj34F2!V?WWIl=`Sqs#uTnmZ`rn|quffMnG``LK3_gzJZwA2JpW?SE9%jUkYohU8 zwEuaE-PE7xUnT&|BMcm$NO7LgKc0+3cv4q00P!}O&v=SSTvCsG+CQ7(V#<3cE}=Zh zpZL?f}?u!M0gzXd)iHzVkC>AN+M=`l?gnx@--N-ND zG>spo_$UWbkN7P4muCUs5%z|x)Ke^UQK zipl;VJXw#XT=3uZQNUVlfCV0+m@?tWyXgqaX<-4vY$brP(TupE-A9c=;cT!j*MO|X~~_b zDr#phZ`kiAoe15Tr4@+Jp7!WCfCZ%KSS3#(bH8zg6QSK|gNh0Rh_rf)A%j@fGos0X zfFy^jLA8r7<2m~1GWQ$PT1T{CAg=1NfLy@tM7-1Pb+b;66+|2Mat`beS+~pS&E#!P zuOM=q+s)c}Ui68a)5aqgkCE5z^C6CNWjbhNVydF(cYgfxZ7|ertELK8H54R$Bh>EV ze~U7Hw~)8E46AnWkmsnVoooU(FEtblNZw(&RDj36JSl)DqafDO0p&=GNAhD$zu8uZ zY61$1I&0%mB`#F4C{_R5h0^(2acw$qSZ!7gvVgPlQ4ODqer8(j(Kgm?Wvwg_SMA6y z2qKj!Lb6Y(L;+@`qD6mZi(ads1V;hL*eNsunHg}tf?8ADr7`!oN*_<>3iF0#;x%( z04%UGPz>lw77FVM14R+(FrX_?@jikDU1Ok#s|6e~HVX7J(63@aQGL)a2vDn%bfFw; z3=Byj%)sd=5MbbRS%GBm0Fq=_r7E%?29WG$;B>62ikc-VY7psA!$Me<^cn_E4-JRY z%JK@POwG?LDJv?NHYIoLaOkGTVhBh`mx?1O^9GEmfXBvinVs_0nI6%}eaN%Vh+gJfCpN^&#| z1A{!rz#vkE@+@O@Gcc%!Wt`>5GFBylfg;jsWT9MDWF@S@uv{g{KB$wkB~n}?4IzNE z^3G%=MP{HVh__=|fl5%)p{SxDh9O0gb*wTl*x4SyNW7f{LP%0TbuF+>wbSPfG)e-a z&micR$@}C^Gx;PnT9O(7PV`PqTF|G;`Q+Nj$(*ZcV~KB4SQnuSAWlRMhaDk=Gm(QA zeH`l+L>6He@_Owqo)^8m!)LQ)+I&2AW90U_y&TKi?KUyfVMF%lut}nbMC1~E4#DoT z*#s`rr`Hy172xp8rOZvBkoXS^fTQK@ego=EAB1~&G)l3y{j^{YKY9EsPn`}8?cvstQN za_O11z5X)ix%5RHGc$^}A3l2O{nP2EhBvppirUwm8M=JQ%FW-c$=mU{W73grk3CQ` z{@YVcC6`T!kFHoh{+L*r)4T7&^#}9vOAr1SGVfXAf3vyp*rYjN+r)eO;Iiy4*N?B< zIaeE8n6fo8<;#Zs=RDakW7)#pgC1M5=RUXfg6I0lxcrTW)F($RD%-N*+v$h;oWIy@ z+tLolKJJam#pSc$w7r${8Wz8F<6P5(qYq3Be4cjY)TRx8J@==X?X4+og`>B|?S5fH z-LVPRGM6-beD=__hP2eYr=RO_=EaK9-*~?K;i=7YUR%*;`zu>^KC~W}ravyu|AF^C zF`~z`hnKzbY0Jem*CXe~IV|k)0{y=H6%Q^QHuSX}_x~;UY^v?O#=e)iVRA~Ic6G;* zNY8qqU+SCnxmWqfOV;aC4t`bUb-4FGIrjHUdN-jd<(5a~+{n9IA4=~PH^2M5f$Q$6 z({om(CGG<5+LJlm1~v~^-SAnp>+Cd7vwd0Di-#OF18UaYd!(`Cu@grt_Rfnuo{{xp z_UyXJ$Nx}n8uF`yrofnnQfcS?yQ}O=Urzpfb;XDk!(rvIr~lCfyz=sxU#ET0vO0f8 z`s8l2I^OGlU=2P}qPjB<4oQAF#SRzn@xGj_{Z`ur*BtwfNplacpLhORsb}elK=}JF zc8@4IndmalZvWkurnbj#%n)iG9 zE^BG8+B>X!?#0%UL6$2!dt0uh&HVJ(`6sUSEp02f)PKmDwGUpKpLj0EvoFc-dv?!( z-z;ezdg&?vk*%+GjD+A|@e8#B?z{BSO5HauZR5VfN5nMCH`U}@ fd_closer (fd, close); + + // Seek to the xz Stream Footer. We assume that it's the last thing in the + // file, which is true for RPM and deb files. + off_t footer_pos = -LZMA_STREAM_HEADER_SIZE; + if (lseek (fd, footer_pos, SEEK_END) == -1) + return false; + + // Decode the Stream Footer. + uint8_t footer[LZMA_STREAM_HEADER_SIZE]; + size_t footer_read = 0; + while (footer_read < sizeof (footer)) + { + ssize_t bytes_read = read (fd, footer + footer_read, + sizeof (footer) - footer_read); + if (bytes_read < 0) + { + if (errno == EINTR) + continue; + return false; + } + if (bytes_read == 0) + return false; + footer_read += bytes_read; + } + + lzma_stream_flags stream_flags; + lzma_ret ret = lzma_stream_footer_decode (&stream_flags, footer); + if (ret != LZMA_OK) + return false; + + // Seek to the xz Index. + if (lseek (fd, footer_pos - stream_flags.backward_size, SEEK_END) == -1) + return false; + + // Decode the Number of Records in the Index. liblzma doesn't have an API for + // this if you don't want to decode the whole Index, so we have to do it + // ourselves. + // + // We need 1 byte for the Index Indicator plus 1-9 bytes for the + // variable-length integer Number of Records. + uint8_t index[10]; + size_t index_read = 0; + while (index_read == 0) { + ssize_t bytes_read = read (fd, index, sizeof (index)); + if (bytes_read < 0) + { + if (errno == EINTR) + continue; + return false; + } + if (bytes_read == 0) + return false; + index_read += bytes_read; + } + // The Index Indicator must be 0. + if (index[0] != 0) + return false; + + lzma_vli num_records; + size_t pos = 0; + size_t in_pos = 1; + while (true) + { + if (in_pos >= index_read) + { + ssize_t bytes_read = read (fd, index, sizeof (index)); + if (bytes_read < 0) + { + if (errno == EINTR) + continue; + return false; + } + if (bytes_read == 0) + return false; + index_read = bytes_read; + in_pos = 0; + } + ret = lzma_vli_decode (&num_records, &pos, index, &in_pos, index_read); + if (ret == LZMA_STREAM_END) + break; + else if (ret != LZMA_OK) + return false; + } + + if (verbose > 3) + obatched(clog) << rps << " has " << num_records << " xz Blocks" << endl; + + // The file is only seekable if it has more than one Block. + return num_records > 1; +} + // Read the Index at the end of an xz file. static lzma_index* read_xz_index (int fd) @@ -2333,6 +2436,11 @@ extract_from_seekable_archive (const string& srcpath, } } #else +static bool +is_seekable_archive (const string& rps, struct archive* a) +{ + return false; +} static int extract_from_seekable_archive (const string& srcpath, char* tmppath, @@ -4282,6 +4390,7 @@ archive_classify (const string& rps, string& archive_extension, int64_t archivei sqlite_ps& ps_upsert_buildids, sqlite_ps& ps_upsert_fileparts, sqlite_ps& ps_upsert_file, sqlite_ps& ps_lookup_file, sqlite_ps& ps_upsert_de, sqlite_ps& ps_upsert_sref, sqlite_ps& ps_upsert_sdef, + sqlite_ps& ps_upsert_seekable, time_t mtime, unsigned& fts_executable, unsigned& fts_debuginfo, unsigned& fts_sref, unsigned& fts_sdef, bool& fts_sref_complete_p) @@ -4336,6 +4445,10 @@ archive_classify (const string& rps, string& archive_extension, int64_t archivei if (verbose > 3) obatched(clog) << "libarchive scanning " << rps << " id " << archiveid << endl; + bool seekable = is_seekable_archive (rps, a); + if (verbose> 2 && seekable) + obatched(clog) << rps << " is seekable" << endl; + bool any_exceptions = false; while(1) // parse archive entries { @@ -4357,6 +4470,10 @@ archive_classify (const string& rps, string& archive_extension, int64_t archivei if (verbose > 3) obatched(clog) << "libarchive checking " << fn << endl; + int64_t seekable_size = archive_entry_size (e); + int64_t seekable_offset = archive_filter_bytes (a, 0); + time_t seekable_mtime = archive_entry_mtime (e); + // extract this file to a temporary file char* tmppath = NULL; rc = asprintf (&tmppath, "%s/debuginfod-classify.XXXXXX", tmpdir.c_str()); @@ -4448,6 +4565,15 @@ archive_classify (const string& rps, string& archive_extension, int64_t archivei .bind(5, mtime) .bind(6, fileid) .step_ok_done(); + if (seekable) + ps_upsert_seekable + .reset() + .bind(1, archiveid) + .bind(2, fileid) + .bind(3, seekable_size) + .bind(4, seekable_offset) + .bind(5, seekable_mtime) + .step_ok_done(); } else // potential source - sdef record { @@ -4461,11 +4587,19 @@ archive_classify (const string& rps, string& archive_extension, int64_t archivei } if ((verbose > 2) && (executable_p || debuginfo_p)) - obatched(clog) << "recorded buildid=" << buildid << " rpm=" << rps << " file=" << fn + { + obatched ob(clog); + auto& o = ob << "recorded buildid=" << buildid << " rpm=" << rps << " file=" << fn << " mtime=" << mtime << " atype=" << (executable_p ? "E" : "") << (debuginfo_p ? "D" : "") - << " sourcefiles=" << sourcefiles.size() << endl; + << " sourcefiles=" << sourcefiles.size(); + if (seekable) + o << " seekable size=" << seekable_size + << " offset=" << seekable_offset + << " mtime=" << seekable_mtime; + o << endl; + } } catch (const reportable_exception& e) @@ -4496,6 +4630,7 @@ scan_archive_file (const string& rps, const stat_t& st, sqlite_ps& ps_upsert_de, sqlite_ps& ps_upsert_sref, sqlite_ps& ps_upsert_sdef, + sqlite_ps& ps_upsert_seekable, sqlite_ps& ps_query, sqlite_ps& ps_scan_done, unsigned& fts_cached, @@ -4533,7 +4668,7 @@ scan_archive_file (const string& rps, const stat_t& st, string archive_extension; archive_classify (rps, archive_extension, archiveid, ps_upsert_buildids, ps_upsert_fileparts, ps_upsert_file, ps_lookup_file, - ps_upsert_de, ps_upsert_sref, ps_upsert_sdef, // dalt + ps_upsert_de, ps_upsert_sref, ps_upsert_sdef, ps_upsert_seekable, // dalt st.st_mtime, my_fts_executable, my_fts_debuginfo, my_fts_sref, my_fts_sdef, my_fts_sref_complete_p); @@ -4639,6 +4774,9 @@ scan () sqlite_ps ps_r_upsert_sdef (db, "rpm-sdef-insert", "insert or ignore into " BUILDIDS "_r_sdef (file, mtime, content) values (" "?, ?, ?);"); + sqlite_ps ps_r_upsert_seekable (db, "rpm-seekable-insert", + "insert or ignore into " BUILDIDS "_r_seekable (file, content, type, size, offset, mtime) " + "values (?, ?, 'xz', ?, ?, ?);"); sqlite_ps ps_r_query (db, "rpm-negativehit-query", "select 1 from " BUILDIDS "_file_mtime_scanned where " "sourcetype = 'R' and file = ? and mtime = ?;"); @@ -4681,6 +4819,7 @@ scan () ps_r_upsert_de, ps_r_upsert_sref, ps_r_upsert_sdef, + ps_r_upsert_seekable, ps_r_query, ps_r_scan_done, fts_cached, diff --git a/tests/Makefile.am b/tests/Makefile.am index cfed54b7..aee5413f 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -227,7 +227,7 @@ export ELFUTILS_DISABLE_DEMANGLE = 1 endif if LZMA -TESTS += run-readelf-s.sh run-dwflsyms.sh +TESTS += run-readelf-s.sh run-dwflsyms.sh run-debuginfod-seekable.sh endif if HAVE_ZSTD @@ -630,6 +630,10 @@ EXTRA_DIST = run-arextract.sh run-arsymtest.sh run-ar.sh \ debuginfod-rpms/rhel7/hello2-debuginfo-1.0-2.x86_64.rpm \ debuginfod-rpms/rhel7/hello2-two-1.0-2.x86_64.rpm \ debuginfod-rpms/rhel7/hello2-two-1.0-2.x86_64.rpm \ + debuginfod-rpms/seekable-xz/compressme-seekable-xz-1.0-1.src.rpm \ + debuginfod-rpms/seekable-xz/compressme-seekable-xz-1.0-1.x86_64.rpm \ + debuginfod-rpms/seekable-xz/compressme-seekable-xz-debuginfo-1.0-1.x86_64.rpm \ + debuginfod-rpms/seekable-xz/compressme-seekable-xz-debugsource-1.0-1.x86_64.rpm \ debuginfod-ima/koji/arch/hello-2.10-9.fc38.x86_64.rpm \ debuginfod-ima/koji/data/sigcache/keyid/arch/hello-2.10-9.fc38.x86_64.rpm.sig \ debuginfod-ima/koji/fedora-38-ima.pem \ @@ -640,6 +644,11 @@ EXTRA_DIST = run-arextract.sh run-arsymtest.sh run-ar.sh \ debuginfod-debs/hithere_1.0-1.dsc \ debuginfod-debs/hithere_1.0-1_amd64.deb \ debuginfod-debs/hithere_1.0.orig.tar.gz \ + debuginfod-debs/seekable-xz/compressme-seekable-xz_1.0-1_amd64.deb \ + debuginfod-debs/seekable-xz/compressme-seekable-xz_1.0-1.debian.tar.xz \ + debuginfod-debs/seekable-xz/compressme-seekable-xz_1.0-1.dsc \ + debuginfod-debs/seekable-xz/compressme-seekable-xz_1.0.orig.tar.xz \ + debuginfod-debs/seekable-xz/compressme-seekable-xz-dbgsym_1.0-1_amd64.deb \ debuginfod-tars/hello-1-1-x86_64.pkg.tar.xz \ debuginfod-tars/hello-debug-1-1-x86_64.pkg.tar.bz2 \ debuginfod-tars/pacman-sources/PKGBUILD \ @@ -669,7 +678,7 @@ EXTRA_DIST = run-arextract.sh run-arsymtest.sh run-ar.sh \ testfile-dwp-4-cu-index-overflow.dwp.bz2 \ testfile-dwp-cu-index-overflow.source \ testfile-define-file.bz2 \ - testfile-sysroot.tar.bz2 run-sysroot.sh + testfile-sysroot.tar.bz2 run-sysroot.sh run-debuginfod-seekable.sh if USE_VALGRIND diff --git a/tests/debuginfod-debs/seekable-xz/compressme-seekable-xz-dbgsym_1.0-1_amd64.deb b/tests/debuginfod-debs/seekable-xz/compressme-seekable-xz-dbgsym_1.0-1_amd64.deb new file mode 100644 index 0000000000000000000000000000000000000000..75cb984c8a071373e64ee9d78a2956b813796009 GIT binary patch literal 6288 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 literal 0 HcmV?d00001 diff --git a/tests/debuginfod-debs/seekable-xz/compressme-seekable-xz_1.0-1.debian.tar.xz b/tests/debuginfod-debs/seekable-xz/compressme-seekable-xz_1.0-1.debian.tar.xz diff --git a/tests/debuginfod-debs/seekable-xz/compressme-seekable-xz_1.0-1.dsc b/tests/debuginfod-debs/seekable-xz/compressme-seekable-xz_1.0-1.dsc new file mode 100644 index 00000000..5ba2559e --- /dev/null +++ b/tests/debuginfod-debs/seekable-xz/compressme-seekable-xz_1.0-1.dsc @@ -0,0 +1,19 @@ +Format: 3.0 (quilt) +Source: compressme-seekable-xz +Binary: compressme-seekable-xz +Architecture: any +Version: 1.0-1 +Maintainer: Omar Sandoval +Standards-Version: 4.5.1 +Build-Depends: debhelper-compat (= 13) +Package-List: + compressme-seekable-xz deb misc optional arch=any +Checksums-Sha1: + bb182efecbbe8a3f9921b411201203711bd66722 7160 compressme-seekable-xz_1.0.orig.tar.xz + 76d7e5457e8cafda6c2d5591ea6701ec39d2be56 1440 compressme-seekable-xz_1.0-1.debian.tar.xz +Checksums-Sha256: + e221d529467a253ddab35e735d1e049826292a3bd395e8f65f690919a9b508d6 7160 compressme-seekable-xz_1.0.orig.tar.xz + 71c40c722a9ff5cace7226a5c75d56dfe11aa220bf5a95a163438a971649e056 1440 compressme-seekable-xz_1.0-1.debian.tar.xz +Files: + bd331eb0439afb87ecef108c7ef6652e 7160 compressme-seekable-xz_1.0.orig.tar.xz + 2ce6642053700808bb65226dde0b6603 1440 compressme-seekable-xz_1.0-1.debian.tar.xz diff --git a/tests/debuginfod-debs/seekable-xz/compressme-seekable-xz_1.0-1_amd64.deb b/tests/debuginfod-debs/seekable-xz/compressme-seekable-xz_1.0-1_amd64.deb new file mode 100644 index 0000000000000000000000000000000000000000..6900119ae0bd20c550b3c78f62eb3a2c26e77df4 GIT binary patch literal 6208 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 literal 0 HcmV?d00001 diff --git a/tests/debuginfod-debs/seekable-xz/compressme-seekable-xz_1.0.orig.tar.xz b/tests/debuginfod-debs/seekable-xz/compressme-seekable-xz_1.0.orig.tar.xz diff --git a/tests/debuginfod-rpms/seekable-xz/compressme-seekable-xz-1.0-1.src.rpm b/tests/debuginfod-rpms/seekable-xz/compressme-seekable-xz-1.0-1.src.rpm new file mode 100644 index 0000000000000000000000000000000000000000..ada76efcf3d952a33f268323bbd49150152a909b GIT binary patch literal 15880 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 vlog$PORT1 2>&1 & +PID1=$! +tempfiles vlog$PORT1 +errfiles vlog$PORT1 + +wait_ready $PORT1 'ready' 1 +wait_ready $PORT1 'thread_work_total{role="traverse"}' 1 +wait_ready $PORT1 'thread_work_pending{role="scan"}' 0 +wait_ready $PORT1 'thread_busy{role="scan"}' 0 + +# Mapping from build ID to sha256 of executable and debuginfo files. Generated with: +# +# #/bin/bash +# set -e +# +# tmpdir="$(mktemp -d)" +# trap 'rm -rf "$tmpdir"' EXIT +# mkdir "$tmpdir/rpm" "$tmpdir/deb" +# rpm2cpio tests/debuginfod-rpms/seekable-xz/compressme-seekable-xz-1.0-1.x86_64.rpm | cpio -D "$tmpdir/rpm" -id --quiet +# rpm2cpio tests/debuginfod-rpms/seekable-xz/compressme-seekable-xz-debuginfo-1.0-1.x86_64.rpm | cpio -D "$tmpdir/rpm" -id --quiet +# ar p tests/debuginfod-debs/seekable-xz/compressme-seekable-xz_1.0-1_amd64.deb data.tar.xz | tar -C "$tmpdir/deb" -xJ +# ar p tests/debuginfod-debs/seekable-xz/compressme-seekable-xz-dbgsym_1.0-1_amd64.deb data.tar.xz | tar -C "$tmpdir/deb" -xJ +# +# echo "declare -A files=(" +# for which in rpm deb; do +# cd "$tmpdir/$which/usr/bin" +# echo " # $which" +# for file in $(ls -v); do +# build_id="$(eu-readelf -n "$file" | sed -n 's/^.*Build ID: \([a-f0-9]\+\).*$/\1/p')" +# executable_sha="$(sha256sum "$file" | cut -d' ' -f1)" +# debuginfo_sha="$(sha256sum "../../usr/lib/debug/.build-id/${build_id:0:2}/${build_id:2}.debug" | cut -d' ' -f1)" +# echo " [$build_id]=\"$executable_sha $debuginfo_sha\" # $file" +# done +# done +# echo ")" +declare -A files=( + # rpm + [3a54b25d643025aa69d33f08f1ddeee42b63b0c7]="7e8f2bb564e1a74f1fb0344b2218ee5dd885a84885d850846980d1e7e56d8b8b 4421819cac8118e56f9fe2fa6f3becb209b115c6eb2906ed54935f970034315c" # compressme1 + [aa1dd872c917955a95be7943219a4c58886dc965]="e38b0b8494c5cb7816394c00e9c80333262d28b368c8eff59397981435e401b4 101f46d227db71ec8c080de08fd93750a976299a81a2397f6f3b19c0771e138a" # compressme2 + [c80ba826295ca18732ddc15474f166c50c81fc51]="7b1fbbe1d702770d8fa22610a9463c41c7dee8d21fce167a9a1b0588bf82f516 49962d52bd736b63975ade48f52b5431fa7f478baf102b12bbb9efce8c5ef0ba" # compressme3 + [f8617b5ea038d166417357779f6c17dec8b80690]="4bc682ee3194ed9d2efb92e828a9f6ff3c7b0f25cb5ba94832250e79261ada8b eec384c131ce68eeb8026168a6888e3acb2fbf0d60fe808ddbe0e342eabf74a9" # compressme4 + [34880de6319ba33c23c1b1c25e454abf5ec9c433]="c83e1ed93fe09b3850368bff92ed9d4e5807515920126db71bdefc25cc3cb617 7fde181eb2ecd79be1b8aa8c5929454df5bf6c91c96e458b7c36df8da9cc640b" # compressme5 + [1be17d4e02bcf6059481e9591881c6ef2d24b795]="94037ba19019ea1be06ffa20f4d9e7cc58faf7af90c4554a657395fcc86e3c3f 6e0c3b6c5daa824f30ea95587e8e3c051bebd0eca883f8cdeada5190e8c1d4cd" # compressme6 + [be3a4ca9a68fc2b200fe5acbb12f0c5b8c761b69]="c16ac0ccde84cd8f89eebec8c29ce783a7da5c5730c76393774925487e6511f7 34b08a88f131cc9d4f7f1053b26dd277956eb18a47420aa1ac8d35c99c23d574" # compressme7 + [d64dd065e26a876c79f9ce640e107075263e4595]="53ab9909861aa77eb5cb5e7d62e2882f733861fcaf5c2421800758e52c1e0dea 3d2e3a6ddd7673acaf0573f39f0cc52d95a1b52a080d34efcc02d85e788c148e" # compressme8 + [28cc53dd47f9d12673d97ab38f6a21bcad3a528f]="9c231d8ea65133479eed17425e1d3b355703bcd0bf3dfc80520e25ffdc6d5d78 79aa232366e99bcbf4adf8ee74b333979d9164f45ad6ba55abb0e85be2ccd079" # compressme9 + [a05381ea7253b6b446e2120cab2fef08445612a3]="a66b503b4decade17b1551ef82b380c1506713b36a78c776d12d9c3863a4115a 0a6d59c228e74d485e9b314cb7e1f718267103d2c179efac89e874f7b852bbec" # compressme10 + # deb + [f2d910ae1e3e3fa717ef202120966ee4dba07ebd]="e16a38135865eff8f26b5ddfdd5719ba4b90d233f469fb07428cc6ef299214e9 047f7ec51840f6cf6977d6253ccd503f2ce2711813a53317b81e79d20452fd38" # compressme1 + [65e773f11929c579b90923ea81f36523ce2337e6]="00e2fac30ba6c494473fdba1e5ccb0cdbf12229581d2f95af519c9550af5d3d6 eee11a8c840623093de5a1518e673e50141df42e18ea1933c78edf7f11742427" # compressme2 + [a679eaf745a6da111041d208cdeb4474192c2e50]="194b79bcab83fa8140ccbee6cc199f233b0e8804b4a2231695f3c7c4604b67b4 684ff9ad385ab15a066ad4bfc3074df15f1525580310202150d7043513e9890b" # compressme3 + [8b117108d9b7f6ffce251421bdf6c6cc8c801d35]="edf8fec536efb1d9cfb534fd67c12a35b60fdb5955d3f9119ef664818fef0b22 1ef9d762fe59b03547ac7baa5596e13a4bfa8322bbdd8400987f8a95a4d038a6" # compressme4 + [2269081378c82ff119d0f0ec8cdaba3977746835]="77a5b384554a32c73f528abfd196c05c5eca6b97c948ebdccbd6d1beb441105b d29dc24c52cde04397f874660e0980f2d4a043ff132c019d0f92b238012ab464" # compressme5 + [d0840f88ceb63f52354c0b4dceda6c5011155bbd]="523c2c1c4176149a2d773307e5de520195195020bf0f2e42eb47b958b2fbdb93 170a684806ff7e55938d3afa02095a6bdd3184b62b8efdfebb72bb85bfc4120b" # compressme6 + [8101300d5d590b434b798c3edda0908346230cef]="878b7b8cedca8fb69ebc8bc3cb635a7ce0dd4b4da787cde18ff7d06503105c73 86d2cd52026cba35114201d3a1fc2ce31b2403d91785dad5366b5a5b9fe3636f" # compressme7 + [b8755c57a65fe12fa210f216dd861cf2b5101918]="2ec562f19b5b57036b48cd1f07d2d61c6a5a91b0470d14393b8c11429753d632 1ee7340245db704a0e3d5f7431706e14e7feeb9ba5c6572e61287b24b0e9fca0" # compressme8 + [ffe01fd7b7d994d9b9d9e9311ac5189d82162ba0]="cf129420e315943b9d63891455aae7fb69189158b66eba82faf1580e763aa642 3a73ccbd08a6a4088355e973bd4748d99c56b3557b12417e4fa34ed9e85c85a9" # compressme9 + [dd25b00a86c89feaf5ba61fd9c6dc8bd9e5aebef]="5bd04cadb7bce5ca3f817b84c734639764046365a96a0bc1870ecc480e7c38a9 08f9da5788224b8cfdc3bd91b784d1f9a109780100a71f6fbe0b52ec43ea436e" # compressme10 +) + +which=(executable debuginfo) +check_all() { + local port="$1" + for build_id in "${!files[@]}"; do + sha=(${files["$build_id"]}) + # Check the executable and the debuginfo. + for ((i = 0; i < 2; i++)); do + # Check each one twice to test the fdcache. + for ((j = 0; j < 2; j++)); do + path="$(env LD_LIBRARY_PATH=$ldpath DEBUGINFOD_URLS=http://localhost:$port ${abs_builddir}/../debuginfod/debuginfod-find "${which[$i]}" "$build_id")" + sha256sum --check - << EOF +${sha[$i]} $path +EOF + rm -f "$path" + done + done + done +} + +check_all $PORT1 + +# Make sure all extractions used the seekable optimization. +curl -s http://localhost:$PORT1/metrics | awk ' +/^http_responses_total\{result="seekable xz archive"\}/ { + print + seekable = $NF +} + +/^http_responses_total\{result="archive fdcache"\}/ { + print + fdcache = $NF +} + +/^http_responses_total\{result="(rpm|deb) archive"\}/ { + print + full = $NF +} + +END { + if (seekable == 0) { + print "error: no seekable extractions" > "/dev/stderr" + exit 1 + } + if (fdcache == 0) { + print "error: no fdcache hits" > "/dev/stderr" + exit 1 + } + if (full > 0) { + print "error: " full " full extractions" > "/dev/stderr" + exit 1 + } +}' + +tempfiles $DB* + +kill $PID1 +wait $PID1 +PID1=0 + +exit 0 From patchwork Tue Jul 23 22:35:43 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Omar Sandoval X-Patchwork-Id: 94391 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 3965A3858C98 for ; Tue, 23 Jul 2024 22:36:32 +0000 (GMT) X-Original-To: elfutils-devel@sourceware.org Delivered-To: elfutils-devel@sourceware.org Received: from mail-oa1-x2d.google.com (mail-oa1-x2d.google.com [IPv6:2001:4860:4864:20::2d]) by sourceware.org (Postfix) with ESMTPS id AB13F385840A for ; Tue, 23 Jul 2024 22:36:03 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org AB13F385840A Authentication-Results: sourceware.org; dmarc=none (p=none dis=none) header.from=osandov.com Authentication-Results: sourceware.org; spf=none smtp.mailfrom=osandov.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org AB13F385840A Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=2001:4860:4864:20::2d ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1721774166; cv=none; b=wfIDkjMsmD8W2A7V5gqGo4cXiOzgX6AkZPUGtVcDhC1N9ZuEusJoT71gIiSJexsCnuBX5dQH9S3aHyXSivFGdGekwayzjE8Oq5sPlKu475K42oV50jFlhMuP3PPDSWAiP+nwE47YULdeVgdLn1LJ72OButiuvLaqj+y+KlDUIWE= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1721774166; c=relaxed/simple; bh=BfGpjmGuoVX1MQUaPOxTnK/QUxIgLJspepRX69c4aAA=; h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version; b=kRnZovZWQf56H05SjW0zu+SejRcn9KE8Utq7dZ1svyCgTonCnic/Yk9NUGtIQWxeRb70g1TSrK23E6HMdxCM3YHAgfh1FgT366MpF9bOeNPq0wPoJrBZ0KvQgtU0bkvZWEFAvmpaP9o8MxWZy9xxDYPAOHzOnY2CGuqH8E9SQ10= ARC-Authentication-Results: i=1; server2.sourceware.org Received: by mail-oa1-x2d.google.com with SMTP id 586e51a60fabf-25e3bc751daso3309050fac.3 for ; Tue, 23 Jul 2024 15:36:03 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=osandov-com.20230601.gappssmtp.com; s=20230601; t=1721774163; x=1722378963; darn=sourceware.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=tGeP9/8dOlqvEia1+uTBIxya97s19VhY1EsZjtEjEEA=; b=2erAvcrhp4cz7PV/gLjXIgXXL0RtMJKIL46WW1INB/zwNUDbv13gcIpdAlwmVl3jjf Kzvc7IcA71rTbW2AAKj8/Nk/vYHvvRbAdN0xz3GOSTx1+3xezbWNDA9rfyr73uW1bE4P PFFj1xBLVImoTleUgec7eo9vD61Nbtu+IhLM0fjldXAJU2YuMdilsUmUCsV99xV88tuE mAnj87wYkUiEgbY3eK8SHCqihlzSs7eIrpnYQGHQrQkTTo69KpJ9aH6johA8fACiHkO6 gEz0S3Nv+0y0HSD7/nEPYRbQErPTj/sLUin86TRlnvYnRQN0NEUjnqvBK0Lm4wHhLM00 5j/g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1721774163; x=1722378963; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=tGeP9/8dOlqvEia1+uTBIxya97s19VhY1EsZjtEjEEA=; b=wtSjGGrigZNZAwQcadytKBWRXXc7q8mCIASmPjVZBEeTuxsw2dk3IYxU5pk+KHVcth kWYuIUHTBYEH2+mnAW5AkyDwyFrPOghmJn6XDK/SrKio7/y9Q1UM1B7i93R4/EFIrxkw LqREjAwLw2E/LzWB4Eoj/QukRu9y9sILxy3FSvijlwtjVWnQS1iw7VSAeeNG/f0eL+JH 5Fc1LGQv1uEpB+lmuPQMbgpcyOLCTfLT6k7tCsBnydOwvL/yHAkHQVVVjuhDypiSSWHZ +tziinmrpB+3vGunl2RtZEDb/ChXy/3hXB1+54AYX06gfZD/p9jO+Ao8NOZ0GdBr4t3j pJMA== X-Gm-Message-State: AOJu0YwrOoTXArYnTbOsZteO9mTitUQaFEBYbb2p/SHXoEJDqRvQgX3N XcqJxUbloCW7zAD5402f8Gaot0yTtATyEtdGisCnHqRgk9vVn9Lu8tfx2tkYHy/Zkm2ctkS61tQ M X-Google-Smtp-Source: AGHT+IEJQVDF0Rq765DFkCykIiWb877J7ZNCLCR7g36NI44A/XBcIQAvXhel1QkKqY12rgglP9wPeQ== X-Received: by 2002:a05:6871:207:b0:254:b5b9:354e with SMTP id 586e51a60fabf-26487692f1dmr1173858fac.19.1721774162660; Tue, 23 Jul 2024 15:36:02 -0700 (PDT) Received: from telecaster.thefacebook.com ([2620:10d:c090:500::5:b6d3]) by smtp.gmail.com with ESMTPSA id d2e1a72fcca58-70d241803fdsm4308079b3a.220.2024.07.23.15.36.01 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 23 Jul 2024 15:36:02 -0700 (PDT) From: Omar Sandoval To: elfutils-devel@sourceware.org Cc: "Frank Ch . Eigler" , Aaron Merey , linux-debuggers@vger.kernel.org Subject: [PATCH v5 7/7] debuginfod: populate _r_seekable on request Date: Tue, 23 Jul 2024 15:35:43 -0700 Message-ID: <1429197ef17eaca1c72abb29c4763938a8f0bb10.1721773977.git.osandov@fb.com> X-Mailer: git-send-email 2.45.2 In-Reply-To: References: MIME-Version: 1.0 X-Spam-Status: No, score=-12.2 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, GIT_PATCH_0, RCVD_IN_DNSWL_NONE, SPF_HELO_NONE, SPF_NONE, TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: elfutils-devel@sourceware.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Elfutils-devel mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: elfutils-devel-bounces~patchwork=sourceware.org@sourceware.org From: Omar Sandoval Since the schema change adding _r_seekable was done in a backward compatible way, seekable archives that were previously scanned will not be in _r_seekable. Whenever an archive is going to be extracted to satisfy a request, check if it is seekable. If so, populate _r_seekable while extracting it so that future requests use the optimized path. The next time that BUILDIDS is bumped, all archives will be checked at scan time. At that point, checking again will be unnecessary and this commit (including the test case modification) can be reverted. Signed-off-by: Omar Sandoval --- debuginfod/debuginfod.cxx | 76 +++++++++++++++++++++++++++++--- tests/run-debuginfod-seekable.sh | 48 ++++++++++++++++++++ 2 files changed, 118 insertions(+), 6 deletions(-) diff --git a/debuginfod/debuginfod.cxx b/debuginfod/debuginfod.cxx index 5fe2db0c..fb7873ae 100644 --- a/debuginfod/debuginfod.cxx +++ b/debuginfod/debuginfod.cxx @@ -2740,6 +2740,7 @@ handle_buildid_r_match (bool internal_req_p, } // no match ... look for a seekable entry + bool populate_seekable = ! passive_p; unique_ptr pp (new sqlite_ps (internal_req_p ? db : dbq, "rpm-seekable-query", "select type, size, offset, mtime from " BUILDIDS "_r_seekable " @@ -2749,6 +2750,9 @@ handle_buildid_r_match (bool internal_req_p, { if (rc != SQLITE_ROW) throw sqlite_exception(rc, "step"); + // if we found a match in _r_seekable but we fail to extract it, don't + // bother populating it again + populate_seekable = false; const char* seekable_type = (const char*) sqlite3_column_text (*pp, 0); if (seekable_type != NULL && strcmp (seekable_type, "xz") == 0) { @@ -2840,16 +2844,39 @@ handle_buildid_r_match (bool internal_req_p, throw archive_exception(a, "cannot open archive from pipe"); } - // archive traversal is in three stages, no, four stages: - // 1) skip entries whose names do not match the requested one - // 2) extract the matching entry name (set r = result) - // 3) extract some number of prefetched entries (just into fdcache) - // 4) abort any further processing + // If the archive was scanned in a version without _r_seekable, then we may + // need to populate _r_seekable now. This can be removed the next time + // BUILDIDS is updated. + if (populate_seekable) + { + populate_seekable = is_seekable_archive (b_source0, a); + if (populate_seekable) + { + // NB: the names are already interned + pp.reset(new sqlite_ps (db, "rpm-seekable-insert2", + "insert or ignore into " BUILDIDS "_r_seekable (file, content, type, size, offset, mtime) " + "values (?, " + "(select id from " BUILDIDS "_files " + "where dirname = (select id from " BUILDIDS "_fileparts where name = ?) " + "and basename = (select id from " BUILDIDS "_fileparts where name = ?) " + "), 'xz', ?, ?, ?)")); + } + } + + // archive traversal is in five stages: + // 1) before we find a matching entry, insert it into _r_seekable if needed or + // skip it otherwise + // 2) extract the matching entry (set r = result). Also insert it into + // _r_seekable if needed + // 3) extract some number of prefetched entries (just into fdcache). Also + // insert them into _r_seekable if needed + // 4) if needed, insert all of the remaining entries into _r_seekable + // 5) abort any further processing struct MHD_Response* r = 0; // will set in stage 2 unsigned prefetch_count = internal_req_p ? 0 : fdcache_prefetch; // will decrement in stage 3 - while(r == 0 || prefetch_count > 0) // stage 1, 2, or 3 + while(r == 0 || prefetch_count > 0 || populate_seekable) // stage 1-4 { if (interrupted) break; @@ -2863,6 +2890,43 @@ handle_buildid_r_match (bool internal_req_p, continue; string fn = canonicalized_archive_entry_pathname (e); + + if (populate_seekable) + { + string dn, bn; + size_t slash = fn.rfind('/'); + if (slash == std::string::npos) { + dn = ""; + bn = fn; + } else { + dn = fn.substr(0, slash); + bn = fn.substr(slash + 1); + } + + int64_t seekable_size = archive_entry_size (e); + int64_t seekable_offset = archive_filter_bytes (a, 0); + time_t seekable_mtime = archive_entry_mtime (e); + + pp->reset(); + pp->bind(1, b_id0); + pp->bind(2, dn); + pp->bind(3, bn); + pp->bind(4, seekable_size); + pp->bind(5, seekable_offset); + pp->bind(6, seekable_mtime); + rc = pp->step(); + if (rc != SQLITE_DONE) + obatched(clog) << "recording seekable file=" << fn + << " sqlite3 error: " << (sqlite3_errstr(rc) ?: "?") << endl; + else if (verbose > 2) + obatched(clog) << "recorded seekable file=" << fn + << " size=" << seekable_size + << " offset=" << seekable_offset + << " mtime=" << seekable_mtime << endl; + if (r != 0 && prefetch_count == 0) // stage 4 + continue; + } + if ((r == 0) && (fn != b_source1)) // stage 1 continue; diff --git a/tests/run-debuginfod-seekable.sh b/tests/run-debuginfod-seekable.sh index 4dd3b71e..52a5ab1e 100755 --- a/tests/run-debuginfod-seekable.sh +++ b/tests/run-debuginfod-seekable.sh @@ -141,4 +141,52 @@ kill $PID1 wait $PID1 PID1=0 +if type sqlite3 2>/dev/null; then + # Emulate the case of upgrading from an old server without the seekable + # optimization by dropping the _r_seekable table. + sqlite3 "$DB" 'DROP TABLE buildids10_r_seekable' + + env LD_LIBRARY_PATH=$ldpath ${abs_builddir}/../debuginfod/debuginfod $VERBOSE \ + -d $DB -p $PORT2 -t0 -g0 \ + --fdcache-mbs=100 --fdcache-mintmp=0 --fdcache-prefetch=0 \ + -R -U R D > vlog$PORT2 2>&1 & + PID2=$! + tempfiles vlog$PORT2 + errfiles vlog$PORT2 + + wait_ready $PORT2 'ready' 1 + + check_all $PORT2 + + # The first request per archive has to do a full extraction. Check + # that the rest used the seekable optimization. + curl -s http://localhost:$PORT2/metrics | awk ' +/^http_responses_total\{result="seekable xz archive"\}/ { + print + seekable = $NF +} + +/^http_responses_total\{result="(rpm|deb) archive"\}/ { + print + full = $NF +} + +END { + if (seekable == 0) { + print "error: no seekable extractions" > "/dev/stderr" + exit 1 + } + if (full > 4) { + print "error: too many (" full ") full extractions" > "/dev/stderr" + exit 1 + } +}' + + tempfiles $DB* + + kill $PID2 + wait $PID2 + PID2=0 +fi + exit 0