Skip to content

Commit 58a5e5d

Browse files
committed
Merge remote-tracking branch 'origin/main' into vplan-irphi
2 parents 85548a3 + 420c056 commit 58a5e5d

File tree

7,558 files changed

+433421
-347652
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

7,558 files changed

+433421
-347652
lines changed

.ci/metrics/metrics.py

Lines changed: 219 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import collections
22
import datetime
3+
import dateutil
34
import github
5+
import json
46
import logging
57
import os
68
import requests
@@ -41,7 +43,7 @@
4143
# This means we essentially have a list of workflows sorted by creation date,
4244
# and that's all we can deduce from it. So for each iteration, we'll blindly
4345
# process the last N workflows.
44-
GITHUB_WORKFLOWS_MAX_PROCESS_COUNT = 1000
46+
GITHUB_WORKFLOWS_MAX_PROCESS_COUNT = 2000
4547
# Second reason for the cut: reaching a workflow older than X.
4648
# This means we will miss long-tails (exceptional jobs running for more than
4749
# X hours), but that's also the case with the count cutoff above.
@@ -53,6 +55,18 @@
5355
# by trial and error).
5456
GRAFANA_METRIC_MAX_AGE_MN = 120
5557

58+
# Lists the BuildKite jobs we want to track. Maps the BuildKite job name to
59+
# the metric name in Grafana. This is important not to lose metrics history
60+
# if the workflow name changes.
61+
BUILDKITE_WORKFLOW_TO_TRACK = {
62+
":linux: Linux x64": "buildkite_linux",
63+
":windows: Windows x64": "buildkite_windows",
64+
}
65+
66+
# Number of builds to fetch per page. Since we scrape regularly, this can
67+
# remain small.
68+
BUILDKITE_GRAPHQL_BUILDS_PER_PAGE = 50
69+
5670
@dataclass
5771
class JobMetrics:
5872
job_name: str
@@ -70,6 +84,180 @@ class GaugeMetric:
7084
time_ns: int
7185

7286

87+
def buildkite_fetch_page_build_list(
88+
buildkite_token: str, after_cursor: str = None
89+
) -> list[dict[str, str]]:
90+
"""Fetches a page of the build list using the GraphQL BuildKite API.
91+
92+
Returns the BUILDKITE_GRAPHQL_BUILDS_PER_PAGE last running/queued builds,
93+
or the BUILDKITE_GRAPHQL_BUILDS_PER_PAGE running/queued builds
94+
older than the one pointer by |after_cursor| if provided.
95+
The |after_cursor| value is taken from the previous page returned by the
96+
API.
97+
98+
Args:
99+
buildkite_token: the secret token to authenticate GraphQL requests.
100+
after_cursor: cursor after which to start the page fetch.
101+
102+
Returns:
103+
The most recent builds after cursor (if set) with the following format:
104+
[
105+
{
106+
"cursor": <value>,
107+
"number": <build-number>,
108+
}
109+
]
110+
"""
111+
112+
BUILDKITE_GRAPHQL_QUERY = """
113+
query OrganizationShowQuery {{
114+
organization(slug: "llvm-project") {{
115+
pipelines(search: "Github pull requests", first: 1) {{
116+
edges {{
117+
node {{
118+
builds (state: [CANCELING, CREATING, FAILING, RUNNING], first: {PAGE_SIZE}, after: {AFTER}) {{
119+
edges {{
120+
cursor
121+
node {{
122+
number
123+
}}
124+
}}
125+
}}
126+
}}
127+
}}
128+
}}
129+
}}
130+
}}
131+
"""
132+
query = BUILDKITE_GRAPHQL_QUERY.format(
133+
PAGE_SIZE=BUILDKITE_GRAPHQL_BUILDS_PER_PAGE,
134+
AFTER="null" if after_cursor is None else '"{}"'.format(after_cursor),
135+
)
136+
query = json.dumps({"query": query})
137+
url = "https://graphql.buildkite.com/v1"
138+
headers = {
139+
"Authorization": "Bearer " + buildkite_token,
140+
"Content-Type": "application/json",
141+
}
142+
data = requests.post(url, data=query, headers=headers).json()
143+
# De-nest the build list.
144+
if "errors" in data:
145+
logging.info("Failed to fetch BuildKite jobs: {}".format(data["errors"]))
146+
return []
147+
builds = data["data"]["organization"]["pipelines"]["edges"][0]["node"]["builds"][
148+
"edges"
149+
]
150+
# Fold cursor info into the node dictionnary.
151+
return [{**x["node"], "cursor": x["cursor"]} for x in builds]
152+
153+
154+
def buildkite_get_build_info(build_number: str) -> dict:
155+
"""Returns all the info associated with the provided build number.
156+
157+
Note: for unknown reasons, graphql returns no jobs for a given build,
158+
while this endpoint does, hence why this uses this API instead of graphql.
159+
160+
Args:
161+
build_number: which build number to fetch info for.
162+
163+
Returns:
164+
The info for the target build, a JSON dictionnary.
165+
"""
166+
167+
URL = "https://buildkite.com/llvm-project/github-pull-requests/builds/{}.json"
168+
return requests.get(URL.format(build_number)).json()
169+
170+
171+
def buildkite_get_incomplete_tasks(buildkite_token: str) -> list:
172+
"""Returns all the running/pending BuildKite builds.
173+
174+
Args:
175+
buildkite_token: the secret token to authenticate GraphQL requests.
176+
last_cursor: the cursor to stop at if set. If None, a full page is fetched.
177+
"""
178+
output = []
179+
cursor = None
180+
while True:
181+
page = buildkite_fetch_page_build_list(buildkite_token, cursor)
182+
if len(page) == 0:
183+
break
184+
cursor = page[-1]["cursor"]
185+
output += page
186+
return output
187+
188+
189+
def buildkite_get_metrics(
190+
buildkite_token: str, previously_incomplete: set[int]
191+
) -> (list[JobMetrics], set[int]):
192+
"""Returns a tuple with:
193+
194+
- the metrics recorded for newly completed workflow jobs.
195+
- the set of workflow still running now.
196+
197+
Args:
198+
buildkite_token: the secret token to authenticate GraphQL requests.
199+
previously_incomplete: the set of running workflows the last time this
200+
function was called.
201+
"""
202+
203+
running_builds = buildkite_get_incomplete_tasks(buildkite_token)
204+
incomplete_now = set([x["number"] for x in running_builds])
205+
output = []
206+
207+
for build_id in previously_incomplete:
208+
if build_id in incomplete_now:
209+
continue
210+
211+
info = buildkite_get_build_info(build_id)
212+
metric_timestamp = dateutil.parser.isoparse(info["finished_at"])
213+
for job in info["jobs"]:
214+
# This workflow is not interesting to us.
215+
if job["name"] not in BUILDKITE_WORKFLOW_TO_TRACK:
216+
continue
217+
218+
# Don't count canceled jobs.
219+
if job["canceled_at"]:
220+
continue
221+
222+
created_at = dateutil.parser.isoparse(job["created_at"])
223+
scheduled_at = dateutil.parser.isoparse(job["scheduled_at"])
224+
started_at = dateutil.parser.isoparse(job["started_at"])
225+
finished_at = dateutil.parser.isoparse(job["finished_at"])
226+
227+
job_name = BUILDKITE_WORKFLOW_TO_TRACK[job["name"]]
228+
queue_time = (started_at - scheduled_at).seconds
229+
run_time = (finished_at - started_at).seconds
230+
status = bool(job["passed"])
231+
232+
# Grafana will refuse to ingest metrics older than ~2 hours, so we
233+
# should avoid sending historical data.
234+
metric_age_mn = (
235+
datetime.datetime.now(datetime.timezone.utc) - metric_timestamp
236+
).total_seconds() / 60
237+
if metric_age_mn > GRAFANA_METRIC_MAX_AGE_MN:
238+
logging.warning(
239+
f"Job {job['name']} from workflow {build_id} dropped due"
240+
+ f" to staleness: {metric_age_mn}mn old."
241+
)
242+
continue
243+
244+
metric_timestamp_ns = int(metric_timestamp.timestamp()) * 10**9
245+
workflow_id = build_id
246+
workflow_name = "Github pull requests"
247+
output.append(
248+
JobMetrics(
249+
job_name,
250+
queue_time,
251+
run_time,
252+
status,
253+
metric_timestamp_ns,
254+
workflow_id,
255+
workflow_name,
256+
)
257+
)
258+
259+
return output, incomplete_now
260+
73261
def github_get_metrics(
74262
github_repo: github.Repository, last_workflows_seen_as_completed: set[int]
75263
) -> tuple[list[JobMetrics], int]:
@@ -168,8 +356,24 @@ def github_get_metrics(
168356
created_at = job.created_at
169357
started_at = job.started_at
170358
completed_at = job.completed_at
171-
queue_time = started_at - created_at
172-
run_time = completed_at - started_at
359+
360+
# GitHub API can return results where the started_at is slightly
361+
# later then the created_at (or completed earlier than started).
362+
# This would cause a -23h59mn delta, which will show up as +24h
363+
# queue/run time on grafana.
364+
if started_at < created_at:
365+
logging.info(
366+
"Workflow {} started before being created.".format(task.id)
367+
)
368+
queue_time = datetime.timedelta(seconds=0)
369+
else:
370+
queue_time = started_at - created_at
371+
if completed_at < started_at:
372+
logging.info("Workflow {} finished before starting.".format(task.id))
373+
run_time = datetime.timedelta(seconds=0)
374+
else:
375+
run_time = completed_at - started_at
376+
173377
if run_time.seconds == 0:
174378
continue
175379

@@ -179,7 +383,7 @@ def github_get_metrics(
179383
datetime.datetime.now(datetime.timezone.utc) - completed_at
180384
).total_seconds() / 60
181385
if metric_age_mn > GRAFANA_METRIC_MAX_AGE_MN:
182-
logging.info(
386+
logging.warning(
183387
f"Job {job.id} from workflow {task.id} dropped due"
184388
+ f" to staleness: {metric_age_mn}mn old."
185389
)
@@ -276,23 +480,33 @@ def upload_metrics(workflow_metrics, metrics_userid, api_key):
276480
def main():
277481
# Authenticate with Github
278482
github_auth = Auth.Token(os.environ["GITHUB_TOKEN"])
483+
buildkite_token = os.environ["BUILDKITE_TOKEN"]
279484
grafana_api_key = os.environ["GRAFANA_API_KEY"]
280485
grafana_metrics_userid = os.environ["GRAFANA_METRICS_USERID"]
281486

282487
# The last workflow this script processed.
283488
# Because the Github queries are broken, we'll simply log a 'processed'
284489
# bit for the last COUNT_TO_PROCESS workflows.
285490
gh_last_workflows_seen_as_completed = set()
491+
# Stores the list of pending/running builds in BuildKite we need to check
492+
# at the next iteration.
493+
bk_incomplete = set()
286494

287495
# Enter the main loop. Every five minutes we wake up and dump metrics for
288496
# the relevant jobs.
289497
while True:
290498
github_object = Github(auth=github_auth)
291499
github_repo = github_object.get_repo("llvm/llvm-project")
292500

293-
metrics, gh_last_workflows_seen_as_completed = github_get_metrics(
501+
gh_metrics, gh_last_workflows_seen_as_completed = github_get_metrics(
294502
github_repo, gh_last_workflows_seen_as_completed
295503
)
504+
505+
bk_metrics, bk_incomplete = buildkite_get_metrics(
506+
buildkite_token, bk_incomplete
507+
)
508+
509+
metrics = gh_metrics + bk_metrics
296510
upload_metrics(metrics, grafana_metrics_userid, grafana_api_key)
297511
logging.info(f"Uploaded {len(metrics)} metrics")
298512

.ci/metrics/requirements.lock.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,10 +247,18 @@ pynacl==1.5.0 \
247247
--hash=sha256:a422368fc821589c228f4c49438a368831cb5bbc0eab5ebe1d7fac9dded6567b \
248248
--hash=sha256:e46dae94e34b085175f8abb3b0aaa7da40767865ac82c928eeb9e57e1ea8a543
249249
# via pygithub
250+
python-dateutil==2.9.0.post0 \
251+
--hash=sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3 \
252+
--hash=sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427
253+
# via -r ./requirements.txt
250254
requests==2.32.3 \
251255
--hash=sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760 \
252256
--hash=sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6
253257
# via pygithub
258+
six==1.17.0 \
259+
--hash=sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274 \
260+
--hash=sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81
261+
# via python-dateutil
254262
typing-extensions==4.12.2 \
255263
--hash=sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d \
256264
--hash=sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8

.ci/metrics/requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
pygithub==2.5.0
2+
python-dateutil==2.9.0.post0

.ci/monolithic-linux.sh

Lines changed: 6 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -90,23 +90,6 @@ if [[ "${runtimes}" != "" ]]; then
9090
INSTALL_DIR="${BUILD_DIR}/install"
9191
mkdir -p ${RUNTIMES_BUILD_DIR}
9292

93-
echo "--- cmake runtimes C++03"
94-
95-
cmake -S "${MONOREPO_ROOT}/runtimes" -B "${RUNTIMES_BUILD_DIR}" -GNinja \
96-
-D CMAKE_C_COMPILER="${INSTALL_DIR}/bin/clang" \
97-
-D CMAKE_CXX_COMPILER="${INSTALL_DIR}/bin/clang++" \
98-
-D LLVM_ENABLE_RUNTIMES="${runtimes}" \
99-
-D LIBCXX_CXX_ABI=libcxxabi \
100-
-D CMAKE_BUILD_TYPE=RelWithDebInfo \
101-
-D CMAKE_INSTALL_PREFIX="${INSTALL_DIR}" \
102-
-D LIBCXX_TEST_PARAMS="std=c++03" \
103-
-D LIBCXXABI_TEST_PARAMS="std=c++03" \
104-
-D LLVM_LIT_ARGS="${lit_args}"
105-
106-
echo "--- ninja runtimes C++03"
107-
108-
ninja -vC "${RUNTIMES_BUILD_DIR}" ${runtime_targets}
109-
11093
echo "--- cmake runtimes C++26"
11194

11295
rm -rf "${RUNTIMES_BUILD_DIR}"
@@ -127,7 +110,11 @@ if [[ "${runtimes}" != "" ]]; then
127110

128111
echo "--- cmake runtimes clang modules"
129112

130-
rm -rf "${RUNTIMES_BUILD_DIR}"
113+
# We don't need to do a clean build of runtimes, because LIBCXX_TEST_PARAMS
114+
# and LIBCXXABI_TEST_PARAMS only affect lit configuration, which successfully
115+
# propagates without a clean build. Other that those two variables, builds
116+
# are supposed to be the same.
117+
131118
cmake -S "${MONOREPO_ROOT}/runtimes" -B "${RUNTIMES_BUILD_DIR}" -GNinja \
132119
-D CMAKE_C_COMPILER="${INSTALL_DIR}/bin/clang" \
133120
-D CMAKE_CXX_COMPILER="${INSTALL_DIR}/bin/clang++" \
@@ -140,6 +127,6 @@ if [[ "${runtimes}" != "" ]]; then
140127
-D LLVM_LIT_ARGS="${lit_args}"
141128

142129
echo "--- ninja runtimes clang modules"
143-
130+
144131
ninja -vC "${RUNTIMES_BUILD_DIR}" ${runtime_targets}
145132
fi

.github/workflows/build-ci-container.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,9 @@ jobs:
2727
# The arch names should match the names used on dockerhub.
2828
# See https://github.com/docker-library/official-images#architectures-other-than-amd64
2929
- arch: amd64
30-
runs-on: depot-ubuntu-22.04-16
30+
runs-on: depot-ubuntu-24.04-16
3131
- arch: arm64v8
32-
runs-on: depot-ubuntu-22.04-arm-16
32+
runs-on: depot-ubuntu-24.04-arm-16
3333
steps:
3434
- name: Checkout LLVM
3535
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2

.github/workflows/commit-access-greeter.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ jobs:
1515
if: >-
1616
github.repository_owner == 'llvm' &&
1717
github.event.label.name == 'infra:commit-access-request'
18-
runs-on: ubuntu-22.04
18+
runs-on: ubuntu-24.04
1919
steps:
2020
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
2121
with:

.github/workflows/commit-access-review.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ permissions:
1212
jobs:
1313
commit-access-review:
1414
if: github.repository_owner == 'llvm'
15-
runs-on: ubuntu-22.04
15+
runs-on: ubuntu-24.04
1616
steps:
1717
- name: Fetch LLVM sources
1818
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2

.github/workflows/docs.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ jobs:
6565
fetch-depth: 1
6666
- name: Get subprojects that have doc changes
6767
id: docs-changed-subprojects
68-
uses: tj-actions/changed-files@dcc7a0cba800f454d79fff4b993e8c3555bcc0a8 # v45.0.7
68+
uses: step-security/changed-files@3dbe17c78367e7d60f00d78ae6781a35be47b4a1 # v45.0.1
6969
with:
7070
files_yaml: |
7171
llvm:

0 commit comments

Comments
 (0)