Compare commits

...

246 Commits

Author SHA1 Message Date
amsam0
7ca35e5678 [wpimath] Add limit setters to SlewRateLimiter (#8581)
This is just #7793 with requested changes applied.
2026-02-27 12:53:18 -08:00
Kevin Cooney
613bd88548 [wpilib] Make Preferences Listener not depend on mutable fields (#8607)
The Listener installed by Preferences was referencing m_typePublisher which could be modified by a future call to setNetworkTableInstance(). Instead, reference a local.

Also made Topic.m_handle final, to guarantee that Topic.equals() is thread-safe, and still work after the publisher has been closed.
2026-02-27 12:42:40 -08:00
Stephen Just
e311722637 [ntcore] Handle interrupted save in NetworkServer (#8630)
In NetworkServer::SavePersistent, if the save is interrupted (by robot
power loss, etc), the networktables.json file may be left in an
unhandled state where the file consumed by
NetworkServer::LoadPersistent is not found, but the backup file exists.
In this case, we should attempt to recover the backup file to avoid
losing all persistent data.
2026-02-27 12:39:05 -08:00
sciencewhiz
ae43b8b6dd [cmd] Fix WaitUntilCommand for match time counting down (#8632)
Fixes #8631
Documents that it will return immediately if FMS isn't attached or DS
isn't in practice mode.
Related to change in DS match time behavior that was documented in #8606
2026-02-23 17:11:59 -08:00
sciencewhiz
5ae8ee06dd [build] use local opencv docs element-list (#8633)
Fixes opencv cloudflare blocking gradle javadoc builds
2026-02-23 16:32:28 -08:00
DeltaDizzy
d9eba4bb22 [wpilib] Document zero angle in SingleJointedArmSim (NFC) (#7756)
Fixes #7752

---------

Co-authored-by: sciencewhiz <sciencewhiz@users.noreply.github.com>
Co-authored-by: Tyler Veness <calcmogul@gmail.com>
2026-02-21 14:36:36 -08:00
Jordan Powers
9cd933fa14 [wpiunits] Fix incorrect magnitudes in some MutableMeasure mutations (#8620)
This PR fixes the magnitude units in `MutableMeasure#mut_acc` and
`MutableMeasure#mut_plus`.

Previously, both `mut_acc` and `mut_plus` were setting the base
magnitude using the unit-ed magnitude value. While this would work fine
for base units where `magnitude == baseUnitMagnitude`, this was creating
issues with derived units.

This PR also adds missing tests for the `MutableMeasure` class.
2026-02-20 15:29:53 -08:00
sciencewhiz
77dfad97c6 [ci] Use wpilib docker-run-action fork (#8615) 2026-02-14 11:32:57 -08:00
sciencewhiz
7ac0612397 [hal,wpilib] Update MatchTime docs for teleop and auto mode (NFC) (#8606)
Update Timer and JNI MatchTime functions to latest docs
2026-02-09 17:14:42 -08:00
Tyler Veness
2c5529d714 [wpimath] Speed up pose estimator correction computation (#8574)
In C++, we use a diagonal matrix to avoid an expensive matrix
multiplication. EJML doesn't have a diagonal matrix type, so in Java, we
use a double array and implement the multiplication manually.
2026-02-06 21:47:49 -08:00
Gavin P
7cd3790c7c [wpiunits] Make RPM an alias of RotationsPerMinute (#8595)
Currently the only name for this unit is `RPM`. This caused a bit of
confusion for a couple of my team members when we failed to find an RPM
unit, assuming it would be named `RotationsPerMinute` as is the standard
for almost all other units, such as `RotationsPerSecond`.

No corresponding changes have been made to wpilibc as it seems to
already work this way, with `rpm` being the abbreviation for
`revolutions_per_minute`.
2026-02-06 21:47:08 -08:00
NotTacos
664484306c [glass] Change match times for rebuilt (#8575)
The timer has changed for rebuilt's auto and teleop.
2026-02-06 21:39:12 -08:00
jpokornyiii
0a37317467 [examples] Adding XRP java and cpp examples for Timed Robot (#8599)
Adding an example (one C++ and one Java) for using TimedRobot with the
XRP.
2026-02-06 21:36:35 -08:00
FirstFox3
6b225bb1f1 [wpilib] Fix typo in TimedRobot.getLoopStartTime() docs (#8590)
Fixed a typo in the description of the getLoopStartTime function in both
C++ and Java TimedRobot class.

---------

Co-authored-by: Tyler Veness <calcmogul@gmail.com>
2026-02-05 18:57:49 -07:00
Vasista Vovveti
3d92547d62 [cscore] Fix format specifier on Mac (#8602) 2026-02-05 18:56:52 -07:00
crueter
c89401250f [hal, wpilib] Usage Reporting: QFRCDashboard -> QDash (#8571) 2026-01-15 19:55:55 -07:00
Tyler Veness
8be7720a68 [sysid] Fix crash on partially empty raw data (#8572)
Fixes #8570.
2026-01-15 18:57:38 -07:00
Michael Lesirge
21b5389bbe [wpimath,cmd] Add multi tap boolean stream filter and multi tap trigger modifier (double tap detector) (#8307)
Add a simple tap counting filter for boolean streams. 

The filter activates when the input has risen (transitioned from false
to true, like when a button is tapped) the required number of times
within the time window after the first rising edge. Once activated, the
output remains true as long as the input is true. The tap count resets
when the time window expires or when the input goes false after
activation.

Example usage:
```java
    xbox.a()
      .multiPress(2, 0.2) // Detect a double tap within 0.2 seconds
      .onTrue(Commands.print("Double tapped A button"));
      
     xbox.y()
      .multiPress(2, 0.5) // Detect a double tap within 0.5 seconds
      .whileTrue(Commands.print("Y held after tap").repeatedly());
```

This is not a noise reduction and/or input smoothing filter, but it is
similar in usage to debounce, so I believe it could be considered a
filter, but am open to a better location.

I believe this would be a useful addition, as double/triple tapping a
button is a common control option in games, yet is not often utilized by
newer FRC teams. I believe adding it to WPILib in a standard way will
allow more teams to make the most out of their controls.
2026-01-14 20:22:07 -08:00
Joseph Eng
9e1258440b [wpimath] Fix Rotation3d interpolation and document extrinsic vs intrinsic (#8544)
Documents the extrinsic vs intrinsic semantics of `plus()` and
`minus()`. (`rotateBy()` was documented in [a previous
PR](https://github.com/wpilibsuite/allwpilib/pull/5508))
Fixes usage of `plus()` and `minus()` in `Rotation3d.interpolate()`.
(Fixes #8523)
Fixes incorrect usages of `plus()`, `minus()`, and `rotateBy()`
throughout `Odometry3d`.
Adds explanatory comments for some `plus()`, `minus()`, and `rotateBy()`
operations.
Fixes `TimeInterpolatableBuffer` not using twists for `Pose3d` (this was
just because I happened to notice it, it isn't really related to the PR)

To check all of our usages of `plus()`, `minus()`, and `rotateBy()`, I
marked them as deprecated, checked compile errors from `./gradlew
compileJava`, and then undeprecated them. You can see all of the spots
that showed up (at least on the Java side) by viewing the diff for
241109c.

I wanted to present this alternative to #8526 because the change has its
own quirks, there's little time before kickoff, and there would be no
code-side warning to teams (and mentors) already used to the current
behavior.
2026-01-14 20:16:24 -08:00
Dave Oleksy
812a1b8e1a [hal] Add 2026 REV products for usage reporting (#8567)
Adding REV Easy Swerve and MAXSpline Encoder to usage reporting.

Co-authored-by: Dave Oleksy <dave.oleksy@revrobotics.com>
2026-01-14 20:14:20 -08:00
Kevin-OConnor
18249badc0 Add 2026 game specifics (#8558)
Co-authored-by: Peter Johnson <johnson.peter@gmail.com>
Co-authored-by: Gold856 <117957790+Gold856@users.noreply.github.com>
2026-01-12 19:07:51 -08:00
sciencewhiz
b82d204525 Update vendordep frcYear to 2026 (#8552) 2026-01-06 16:29:45 -08:00
PJ Reiniger
ccb3266753 [upstream_utils] Remove patch that results in building with NDEBUG causing ODR issues (#8540) 2026-01-03 13:31:26 -08:00
sciencewhiz
71a788e20b [docs] Update references to 2026 (#8534) 2026-01-02 08:46:22 -08:00
Charlotte Wilson
f395954d3c [ci] Update Android NDK version to R27D in CMake workflow (#8525)
https://github.com/android/ndk/wiki#release-schedule
2025-12-31 09:08:33 -08:00
Bryce Roethel
ab3af00d07 [wpimath] TrapezoidProfile.State implement StructSerializable (#8499)
Seems like this was missed in #8163
2025-12-21 10:08:32 -06:00
Tyler Veness
1b2f051b4b [docs] Replace instance of PWMSpeedController (#8478) 2025-12-14 08:06:02 -08:00
Benjamin Hall
3dc334c1ee [wpimath] Fix ResetTranslation and ResetRotation in PoseEstimator and PoseEstimator3d causing the robot to teleport (#8285)
Fixes https://github.com/wpilibsuite/allwpilib/issues/8284.

If we have vision updates at the time of the `Reset*` call, we can
correct the translation/rotation of the new odometry pose by adding a
new vision update where:
- `ResetTranslation`: the translation is hard-coded to the new
translation, and the rotation components are set to those of the latest
vision update (prior to clearing the map).
- `ResetRotation`: the rotation is hard-coded to the new rotation, and
the translation components are set to those of the latest vision update
(prior to clearing the map).
2025-12-13 15:53:53 -08:00
Peter Johnson
baa6379267 [wpimath] Add usage reporting for state-space classes (#8453)
- LinearQuadraticRegulator
- Kalman filters
- Pose estimators
- LinearSystemLoop

Fixes #2925.
2025-12-06 09:17:02 -08:00
Peter Johnson
0d1dd84e86 [wpilib] LEDPattern: Add usage reporting (#8452) 2025-12-06 09:16:45 -08:00
Levi
a61866912b [hal] Rename Lumen to Lumyn in usage reporting (#8455) 2025-12-05 19:13:39 -08:00
Peter Johnson
57c40a3dfc [hal] Add more usage reporting constants (#8451)
Fixes #7234 
Fixes #6919
Supports #2925
Supersedes #8212 
Supersedes #7708
2025-12-05 15:20:49 -08:00
Peter Johnson
6f86f533e5 [wpiutil] MemoryBuffer: Fix zero extending size_t warning on Win32 (#8450) 2025-12-05 10:55:28 -08:00
Ryan Blue
ded6790bcd [cmake] Only add wpilibj to generated config if Java is enabled (#8434)
Fixes #8422
2025-11-29 20:44:02 -08:00
Keagan Kautzer
769ce5e9fa [wpiunits] Rename AngularMomentumUnit.mult to per (#8409)
Fixes #8408
2025-11-23 14:40:30 -08:00
Ryan Blue
f6b4ad575b [ci] Pin docker-run-action and remove rm command (#8415)
The fix was made but has not been tagged.
2025-11-22 07:55:44 -08:00
Gold856
7cd0ef5bd9 [docs] Revert "Update readme to say Xcode is required" (#8397)
This reverts commit 49b4b064cf  (#7892).
2025-11-18 17:14:01 -08:00
Warren Reynolds
bd7a88a6d0 [examples] Fix order of Swerve Modules in Odometry Update (#8396)
The order of the Swerve Modules in the m_odometry.Update call needs to
match the order they are defined in the creation of the kDriveKinematics
object.
2025-11-18 17:13:27 -08:00
Benjamin Hall
95cb38e6df [wpimath] Fix ElevatorSim::GetCurrentDraw() in C++ (#8370)
The Kv calculation in C++ was missing a negative sign compared to the Java implementation.
2025-11-13 11:48:43 -07:00
Joseph Eng
b8d6bc2eb1 [wpimath] Scale transforms instead of twists in PoseEstimator (#8333)
The spiraling issue occurs when the vision rotation standard deviation is very high relative to the odometry rotation standard deviation and the vision measurements have a large rotation error. (Scaling the rotation component of a twist without scaling the translation component causes the direction of overall translation to change, leading to spiraling around (either towards or away) the vision measurement instead of moving towards it.) Using a transform instead of a twist avoids this issue.

In general, scaling twist components is more mathematically correct than scaling transform components. However, although twists are correct for modeling uncertainty in an odometry-only pose estimate, they are not correct for the difference between the odometry-only pose estimate and a vision measurement. Since neither twists nor transforms are completely correct (and the pose estimator as a whole is not mathematically correct), but using transforms can guarantee that the pose estimate approaches the vision measurement (instead of potentially spiraling away), they are the least bad option.
2025-11-07 18:07:43 -08:00
Peter Johnson
688535298b [cscore] Add braces to match styleguide (NFC) (#8339) 2025-11-07 18:07:05 -08:00
Thad House
02252b58d7 [build] Update to 2026 Beta 1 (#8337) 2025-11-07 18:06:39 -08:00
Joseph Eng
e207ca4880 [build] Clean up spotbugs excludes (#8332) 2025-11-07 10:07:56 -08:00
Tyler Veness
f4db88da9a [build] Document how to mirror new Doxygen versions (#8327) 2025-11-01 14:45:53 -07:00
Jade
e45aadc851 [sysid] Remove Phoenix5 CANcoder preset (#8316)
Signed-off-by: Jade Turner <spacey-sooty@proton.me>
2025-11-01 09:19:52 -07:00
Dalton Smith
fea6883d98 [wpimath] DCMotor: Add X44 and Minion (#8319) 2025-11-01 09:19:36 -07:00
Tyler Veness
b7fe5ef833 [build] Fix up grammar in docs/build.gradle (#8317) 2025-11-01 09:18:50 -07:00
Tyler Veness
cd237e57d4 [ci] Upgrade to macOS 15 runner image (#8321)
This fixes a compiler bug (rejecting out-of-line definitions of constrained members) newer versions of Sleipnir were encountering.
2025-11-01 09:17:09 -07:00
Murat65536
8b99ad82c3 [wpilib] Add a few unit overloads (#8231)
Co-authored-by: Sam Carlberg <sam@slfc.dev>
Co-authored-by: Joseph Eng <91924258+KangarooKoala@users.noreply.github.com>
Co-authored-by: Tyler Veness <calcmogul@gmail.com>
2025-10-28 20:18:55 -07:00
Thad House
58ba536351 [wpilib] Remove Jaguar (and other) motor controllers (#8298)
https://community.firstinspires.org/2025-robot-rules-preview-for-2026
2025-10-28 20:18:02 -07:00
Tyler Veness
4aef52a117 [ci] Upgrade to wpiformat 2025.36 (#8308)
clang-format 21 made some formatting changes. Since wpiformat's stdlib
task was removed, I removed NOLINT comments for it and removed some
std:: prefixes it added to comments.
2025-10-28 20:17:04 -07:00
Jason Daming
a6a4912a80 [snippets] Add ProfiledPIDController with feedforward snippets (#8280)
Adds snippets demonstrating ProfiledPIDController usage with
SimpleMotorFeedforward using the two-parameter calculate() method
(currentVelocity, nextVelocity).

These snippets will be used in frc-docs to document the recommended
feedforward pattern with ProfiledPIDController.

Co-authored-by: sciencewhiz <sciencewhiz@users.noreply.github.com>
2025-10-27 20:49:16 -06:00
Ryan Blue
873e960e93 [ci] Update tools workflow for 2026 (#8301) 2025-10-25 17:23:12 -07:00
Gold856
4f133c6aa1 [build][ci] Update vcpkg baseline (#8300) 2025-10-25 10:28:39 -07:00
Edan Thomton
35dd61cde5 [build] Fix Eclipse annotation generation in wpilibjExamples (#8295) 2025-10-23 22:29:32 -07:00
Michael Lesirge
9e85f3cf55 [wpimath] Rename 1D copySignPow to match 2D copyDirectionPow (#8286) 2025-10-11 09:24:10 -07:00
Michael Lesirge
2b43541b94 [wpimath] MathUtil: Add 2D variants of applyDeadband and copySignPow (#8057) 2025-10-10 13:44:12 -07:00
arbessette
4c4996e638 [docs] Remove Private Message language (#8202)
Removing private written message for safety of all users and contributors.
2025-10-10 12:43:02 -07:00
Sam Carlberg
cfbd7a5af2 [build] Fix doxygen builds on apple CPUs (#8282)
Caused by the doxygen gradle plugin attempting to download 1.10.0 (presumably its default version) from artifactory because the 1.12.0 config is only applied on x86_64 platforms. Just fixing that isn't enough, however; on mac, the plugin would fail to extract the dmg. We need to fall back to a global installation on the PATH for the plugin to find, preferentially using that instead of a failed attempt to download and extract the dmg.
2025-10-10 12:42:02 -07:00
Tyler Veness
f5990e8b40 [upstream_utils] Fix Eigen tag (#8283)
Upstream no longer seems to have the commit we were pointing to. We'll
just use the tag since that hasn't changed since the official release
announcement.
2025-10-09 21:50:55 -07:00
sciencewhiz
b56b843c8a Update frcYear in vendordeps (#8276) 2025-10-07 22:00:04 -07:00
sciencewhiz
2fb5271cc9 [build] Update native-utils to 2026 (#8277) 2025-10-05 14:22:53 -07:00
Peter Lilley
f1b9be551b [wpiutil] Add reverse/bidirectional iterators to wpi::circular_buffer (#8275)
Use std::reverse_iterator<> to create reverse iterators, make other
iterators bidirectional to allow for this. Added unit tests.
2025-10-03 23:13:55 -07:00
Sam Carlberg
3972b01c51 Add javac plugin for detecting common error cases at compile time (#8196)
Adds a `@NoDiscard` annotation that can be placed on methods to guarantee their return values are used and on types to guarantee that any method returning that type uses the return value.

Methods that call `@NoDiscard`-annotated functions can add a `@SuppressWarnings("NoDiscard")` or `@SuppressWarnings("all")` annotation (or annotation on the class declaring that method) to silence the compiler error warnings.
2025-10-03 17:42:47 -07:00
Joseph Eng
871769c815 [wpimath] Fix units overload resolution (#8267) 2025-10-02 17:36:30 -07:00
Peter Johnson
ca7718cb08 [glass] FMS: Fix reading past end of GSM buffer (#8268) 2025-10-02 17:35:50 -07:00
Joseph Eng
5e7e5306df [wpiutil] Update StructSerializable contract (NFC) (#7441)
Matches ProtobufSerializable.  This is necessary for generic types.
2025-09-30 14:57:42 -06:00
Gold856
6447011bc3 [ci] Consolidate docs jobs (#7910)
We build docs in three different places, which is annoying to deal with, and it means we build docs two more times than necessary. Now, docs are built just once in the main Gradle workflow, with warnings promoted to errors, eliminating the need for the separate job in lint-format.yml. The uploaded docs artifact is then unpacked and commited to the GitHub Pages repo like normal.
2025-09-29 18:02:42 -07:00
Tyler Veness
bb5ee73e46 [build] Build JNI and benchmarks as release by default (#8257)
It doesn't make sense to benchmark debug binaries. Also, wpimath JNI
performance in unit tests is drastically impacted by debug vs release.
2025-09-29 17:47:17 -07:00
Gold856
0277759d44 [wpiunits] Remove redundant if statement and inaccurate comment (#8262) 2025-09-28 16:00:35 -07:00
Gold856
f9899eb73f [wpimath] Fix odd header path in BangBangController (#8261) 2025-09-27 22:48:00 -07:00
Tyler Veness
6b8be313c7 [build] Fix Java 25 builds (#8245)
I'm able to use a local install of Gradle 9.1 that has Java 25 support,
but some plugin upgrades are needed as well.
2025-09-25 21:28:37 -07:00
Tyler Veness
ab53d51c6f Fix or suppress clang-tidy warnings (#8254) 2025-09-25 21:28:04 -07:00
Tyler Veness
5003939b64 [upstream_utils] Recopy Eigen source (#8251)
Upstream slid the tag (again).  Change to the SHA commit ID until things stabilize.
2025-09-24 09:03:16 -06:00
Tyler Veness
ab259c2e89 [build] Fix Gradle 9 archives deprecation warning (#8247)
The deprecation message was:
```
The `archives` configuration added by the `base` plugin has been
deprecated and will be removed in Gradle 10.0.0. Adding artifacts to the
`archives` configuration will now result in a deprecation warning. If
you want the artifact built when running the `assemble` task, you should
add the artifact (or the task that produces it) as a dependency of the
`assemble` task directly.

val specialJar = tasks.register<Jar>("specialJar") {
    archiveBaseName.set("special")
    from("build/special")
}
tasks.named("assemble") {
    dependsOn(specialJar)
}
```
2025-09-22 11:58:14 -06:00
Tyler Veness
8fb5a1985a [upstream_utils] Recopy Eigen source (#8249)
Upstream slid the tag.
2025-09-21 21:58:43 -07:00
sciencewhiz
1e50471d2c [build] Update gradle-jni to 1.2.0 for Gradle 9 support (#8246) 2025-09-21 08:14:37 -07:00
sciencewhiz
c575a23e8e [build] Fix wpical and sysid icons on macOS (#8243)
Fixes #8239
2025-09-20 20:30:44 -07:00
sciencewhiz
4522cca70f [build] Update to develocity plugin (#8242)
Gradle enterprise plugin has been replaced by develocity.
2025-09-20 20:30:05 -07:00
sciencewhiz
850a148aad [build] Fix gradle 9 deprecations in msvc runtime (#8244) 2025-09-20 17:59:29 -07:00
Tyler Veness
0a4e44ea06 [wpimath] Synchronize C++ and Java RK4 docs (NFC) (#8238) 2025-09-20 15:49:41 -07:00
Tyler Veness
a7e7f6912a [upstream_utils] Upgrade to Eigen 5.0.0 (#8240) 2025-09-20 15:49:23 -07:00
Sam Carlberg
ee0a8a1e56 [epilogue] Support logging of protobuf-serializable types (#8229)
For parity with struct-serializable types.

Change struct serialization to only apply to types with a public static final <type> struct field, instead of relying only on the marker interface (which is not always followed). Doing this allows fallthrough to the protobuf handler for types with dynamic structs but static protobuf serializers.
2025-09-20 11:23:22 -07:00
Tyler Veness
3dbdfa1839 [upstream_utils] Upgrade Sleipnir (#8235) 2025-09-20 11:21:06 -07:00
Tyler Veness
ee3d55e848 [upstream_utils] Upgrade Eigen to latest (#8228) 2025-09-19 17:52:48 -06:00
Tyler Veness
dbffe6e8ac [wpimath] Add readme (#8209) 2025-09-08 17:26:22 -07:00
Ryan Blue
2639e0365b [ci] Update sentinel build with Windows FFI changes (#8218) 2025-09-07 21:33:19 -07:00
Ryan Blue
08f11488b0 [ci] Add 2027 development repo to cleanup task (#8217) 2025-09-07 06:16:26 -07:00
Tyler Veness
632749e6f3 [build] Upgrade Maven dependencies (#8173) 2025-09-01 08:13:46 -07:00
Jade
b0829356fa [wpimath] Fix sysid links (NFC) (#8204)
Signed-off-by: Jade Turner <spacey-sooty@proton.me>
2025-09-01 08:11:39 -07:00
Ryan Blue
ed904851eb [ci] Fix CMake Android build caching (#8206)
sccache was enabled but didn't have write credentials
2025-08-31 21:55:13 -07:00
Wispy
2cfd58f119 [commands] Add Subsystem.idle() (#7815) 2025-08-30 22:54:53 -07:00
Sam Carlberg
129cbbe11d [epilogue] Optimize time and memory usage of epilogue backends (#8190) 2025-08-30 20:15:22 -07:00
Ryan Shavell
45db0fd45e [epilogue] Use reflection to access non-public superclass fields (#7996)
Co-authored-by: Sam Carlberg <sam@slfc.dev>
2025-08-30 20:14:41 -07:00
Kevin-OConnor
9fd4ccf95b [hal] Add CAN Mfgrs and Adjust Device Types (#8201) 2025-08-30 11:36:26 -07:00
sciencewhiz
4e6b9706ff [build] Explicitly set Documentation archiveVersion (#8192)
In the recent gradle or gradle dependencies update, the documentation
zips were being published as documentation-version-unspecified, where
the unspecified was coming from archiveVersion. It looks like we use a
different method of setting the version, so make sure that
archiveVersion is empty
2025-08-26 08:09:18 -06:00
Tyler Veness
0d9e850e22 [wpimath] Fix dt type in C++ tests (#8179)
The UKF test was calling `.value()` on an implicit
`units::millisecond_t` type assuming it was `units::second_t`.

I normalized the rest of the dt declarations while I was at it.
2025-08-16 22:51:13 -07:00
sciencewhiz
46a3318324 [build] Fix processstarter publishing (#8171)
Artifacts weren't in OS and architecture subfolders in the zip like all the other
C++ tools
2025-08-11 20:52:10 -07:00
Daniel Chen
f209ecb0cb [wpimath] Add structs for TrapezoidProfile.State and ExponentialProfile.State (#8163) 2025-08-10 11:45:36 -07:00
Iris
78fa67099e [build] Small fixes to build on GCC 15 (#8148)
Co-authored-by: Tyler Veness <calcmogul@gmail.com>
2025-08-09 00:07:41 -07:00
Tyler Veness
9ac7e286f5 [build] Upgrade Gradle plugins (#8166)
I upgraded all plugins I could see except org.ysb33r.doxygen. 2.0 made
breaking changes, and I couldn't figure out how to migrate.

Most of the changes are for suppressing new linter purification rites.
2025-08-08 23:04:02 -07:00
Tyler Veness
5fd9e1e72a [build] Fix Gradle Task.project deprecation warning (#8167)
```
> Task :wpilibcExamples:checkCommands
Script '/home/tav/frc/wpilib/allwpilib/shared/examplecheck.gradle': line 135
Invocation of Task.project at execution time has been deprecated. This will fail with an error in Gradle 10.0. This API is incompatible with the configuration cache, which will become the only mode supported by Gradle in a future release. Consult the upgrading guide for further information: https://docs.gradle.org/8.14.3/userguide/upgrading_version_7.html#task_project
        at examplecheck_4wsg1s37eigy9vs5arzst20ga$_run_closure5$_closure16$_closure17.doCall$original(/home/tav/frc/wpilib/allwpilib/shared/examplecheck.gradle:135)
        (Run with --stacktrace to get the full stack trace of this deprecation warning.)
```

Moving the project access outside the doLast block makes it occur at
confguration time instead.
2025-08-08 22:48:11 -07:00
Tyler Veness
0a0adebd89 [build] Upgrade to Gradle 8.14.3 (#8164)
This fixes local builds with JDK 24.

I fixed deprecation warnings from `./gradlew wrapper --warning-mode all`
as well.
2025-08-08 09:08:34 -06:00
Gold856
2d11946d98 [wpical] Use updated thirdparty-ceres and move resource files (#8151) 2025-08-03 11:41:25 -07:00
Gold856
c42fde5d07 [ci] Make upstream_utils check error with a more clear message (#8153)
Now it links to the README in upstream_utils.
2025-08-03 11:37:40 -07:00
Rain Heuer
b3aeee18c8 [wpimath] Add vector product and squared length operations to Translation2d/3d (#8133)
Adds methods to compute the dot and cross products between Translation2ds and Translation3ds, as well as methods to compute the square of Distance and Norm, which allows avoiding some calls to sqrt in many cases.

Co-authored-by: Tyler Veness <calcmogul@gmail.com>
2025-07-31 21:05:39 -07:00
Tyler Veness
feee88f40d [wpimath] Remove redundant transposes on symmetric matrices (#8131)
This likely won't have a performance impact since it only affects matrix traversal order, but it does simplify the code.
2025-07-31 21:04:55 -07:00
Peter Johnson
0478176e47 [simgui] Add GUI context getter hooks (#8127)
This enables GUI libraries to be linked statically with shared context.
2025-07-30 21:29:24 -07:00
Tyler Veness
e678a338b4 [ci] Upgrade wpiformat (#8124)
See https://github.com/wpilibsuite/styleguide/pull/312
2025-07-30 11:10:12 -06:00
Thad House
b66a66b693 [commands] Add systemcore to command vendor dep (#8108) 2025-07-21 18:38:14 -07:00
Austin Shalit
484cbabc06 [wpinet] Disable Uv FailedLookup test (#8105)
See https://github.com/wpilibsuite/allwpilib/discussions/8104. This test consistently fails when ISPs 'helpfully' resolve an invalid name.
2025-07-20 22:16:28 -07:00
Michael Lesirge
f308e9873a [wpimath] Add miles per hour conversation methods (#8026) 2025-07-17 21:19:32 -07:00
sciencewhiz
8799733838 [processstarter] Handle Elastic and AdvantageScope (#8068)
Since they are in different directories, they need to be special cased
This is needed to support wpilibsuite/WPILibInstaller-Avalonia#492,
since those are currently handled by special scripts.
2025-07-17 21:18:27 -07:00
sciencewhiz
2875fd7d7c [wpilib] Disambiguate HID Sendable names (#8081)
Fixes #7933
Add Axis to triggers sendable name
2025-07-15 21:19:03 -07:00
Joseph Eng
2fc86a155d [benchmark] Add benchmark subproject (#8042) 2025-07-15 21:16:06 -07:00
sciencewhiz
8d7bed791c [ci] Revert Bazel artifactory caching (#8080)
* Revert "[bazel] Set remote cache header to prevent redirects (#8065)"

This reverts commit 082de35ad7.

* Revert "[ci] Add Bazel caching (#8006)"

This reverts commit c3b327d57b.
2025-07-13 21:56:00 -05:00
Tyler Veness
69ecd02aec [upstream_utils] Add timestamps to commit hashes (#8078) 2025-07-10 17:01:15 -05:00
Joseph Eng
6bac1cbc1a [docs] Add information about the development process (NFC) (#8012) 2025-07-08 00:21:23 -05:00
Ryan Blue
082de35ad7 [bazel] Set remote cache header to prevent redirects (#8065)
Doesn’t fully fix the issue but improves things.
2025-07-06 08:28:56 -05:00
Austin Schuh
4b39853483 [build] Check results of subprocess.run in wpimath/generate_quickbuf.py (#8069)
The generator was calling a binary of the wrong architecture and failing
internally, but passing the build (incorrectly).  Explode right away.

Signed-off-by: Austin Schuh <austin.linux@gmail.com>
2025-07-06 07:24:09 -05:00
Gold856
c3b327d57b [ci] Add Bazel caching (#8006) 2025-07-03 23:15:18 -05:00
Gold856
26771e38fb [commands] Deprecate Command.schedule() (#7072)
It's only syntactic sugar over the CommandScheduler's schedule method and creates a footgun because it’s too obvious to try to use in incorrect places.

Co-authored-by: Starlight220 <53231611+Starlight220@users.noreply.github.com>
2025-07-02 13:46:59 -07:00
Gold856
a3ce880334 [ci] Update Homebrew protobuf version for CMake (#8045)
protobuf@3 will be deactivated July 1st, so it's been replaced with protobuf@29.
2025-06-30 00:10:37 -07:00
Nathan Karwel
e6bb29e2f1 [wpilib] Fix typos in notifier comments (NFC) (#8047) 2025-06-30 00:10:08 -07:00
Tyler Veness
633603dc3d [build] Fix Clang 20 warnings (#8048)
* `-Wfixed-enum-extension` was replaced with `-Wc23-extensions`
* Removed unused private variables in SysId
* Suppressed `-Wnontrivial-memcall` in imgui.h and imgui_internal.h
2025-06-30 00:09:41 -07:00
sciencewhiz
f2dd81c839 [build] Add checkstyle check for deprecation (#7738)
Ensures java deprecated notation is paired with javadoc and vice versa.
Adds javadoc deprecation for MecanumControllerCommand, ArmFeedForward,
ElevatorFeedforward, and MecanumDriveMotorVoltages
Fixes #7736
Supersedes #7737

Co-authored-by: Joseph Eng <91924258+KangarooKoala@users.noreply.github.com>
2025-06-30 00:09:02 -07:00
Peter Johnson
ccc9740d56 [glass] Fix NT int64 value display (#8038) 2025-06-27 06:33:31 -07:00
ThePixelatedCat
ffe296892c [wpimath] Fix TrapezoidProfile limiting velocity incorrectly (#8030) 2025-06-24 22:36:53 -07:00
Tyler Veness
ddc5220ed4 [upstream_utils] Remove memory library (#8035)
We added this in 2022. If we haven't used it by now, we probably never
will and it's just wasting space.
2025-06-24 22:35:30 -07:00
Peter Johnson
676f2f84d7 [ntcore] Check id ranges in control messages (#7726) 2025-06-18 21:03:24 -07:00
Dustin Spicuzza
edde38a41a Restore original language from original contributor covenant (#7836)
- https://www.contributor-covenant.org/version/2/0/code_of_conduct/
2025-06-18 20:56:38 -07:00
Kevin Cooney
c655b7a893 [wpilibj] Add Preferences.getNetworkTable() (#7962) 2025-06-16 20:22:11 -07:00
Tyler Veness
057f5ddf81 [glass] Fix compilation errors from C++23 clang (#8008) 2025-06-16 19:01:00 -07:00
Michael Lesirge
fb399eef3d [wpimath] Add copySignPow to MathUtil for joystick input shaping (#8013) 2025-06-15 14:08:41 -07:00
Michael Lesirge
e2517b7a21 [wpimath] Update parameter type from list to general collection (#8020) 2025-06-15 14:08:06 -07:00
Michael Lesirge
05c080328b [wpimath] Pass Translation2d by const reference instead of by value (#8021) 2025-06-13 18:50:05 -07:00
Michael Lesirge
c01e318370 [wpimath] Add Translation3d.nearest() (#8015) 2025-06-12 22:14:00 -07:00
Michael Lesirge
075cc4a20f [wpimath] Add nearest() method to Pose3d (mirroring Pose2d) (#8010) 2025-06-05 22:11:49 -07:00
Ryan Shavell
7a3df6175e [epilogue] Add superclass field & method logging (#7993) 2025-05-31 06:38:51 -07:00
Gold856
ca05ffa1b9 [upstream_utils] Use pathlib instead of os.path (#7983)
A noteworthy change is the replacement of the `dp.startswith(os.path.join(".", "subdir"))` pattern. pathlib doesn't offer something with similar semantics besides `match` and `full_match`, so there's now a helper function that replicates the behavior.

Other notable changes include the addition of type annotations to ensure code correctness, using == to check file names instead of `endswith` for clarity (`endswith` is still used to check extensions), manual walking and copying being refactored in googletest, json, memory, nanopb, protobuf, and sleipnir to use `walk_cwd_and_copy_if`, and matching functions being shortened to the point where they can just be inlined into the lambda.

Co-authored-by: Tyler Veness <calcmogul@gmail.com>
Co-authored-by: David Vo <auscompgeek@users.noreply.github.com>
2025-05-29 16:05:22 -06:00
Tyler Veness
de718f7ae5 [upstream_utils] Upgrade Sleipnir (#7973) 2025-05-27 08:24:15 -06:00
sciencewhiz
5368e8c6ed [ci] Disable RobotPy build (#7990)
Needs update for meson build
2025-05-27 08:19:39 -06:00
Phuc-Thanh Nguyen
abd312f3d0 [wpinet] http_parser: unset F_CHUNKED on new Transfer-Encoding (#7985) 2025-05-22 15:43:20 -06:00
Tyler Veness
b4823569a4 [upstream_utils] Upgrade Eigen to latest (#7982) 2025-05-19 14:26:53 -06:00
Lucien Morey
d32e60233f [wpimath] Add dynamic size support for angle statistics (#7964) 2025-05-15 18:43:46 -07:00
sciencewhiz
79a9d7f987 [examples] Add Accelerometer filtering snippets 2025-05-13 22:03:22 -07:00
sciencewhiz
0877d130be [examples] Add Accelerometer collision detection snippets 2025-05-13 22:03:22 -07:00
sciencewhiz
89555383cc [examples] Add BuiltInAccelerometer Snippet 2025-05-13 22:03:22 -07:00
sciencewhiz
de315947e9 [examples] Add ADXL 345 and 362 snippets 2025-05-13 22:03:22 -07:00
sciencewhiz
b7cd03adc4 [examples] Add AnalogAccelerometer snippets
Delete AnalogAccelerometer comments about sensitivity and zero constants that don't exist
2025-05-13 22:03:22 -07:00
Tyler Veness
cc8eaf3ed7 [wpilib] Remove unhelpful comments from BooleanEvent (#7970)
These were apparently a meme about state being hard to manage rather
than a statement about the code itself. I spent a while trying to find
some complex logic this comment was alluding to that would indicate why
it's "a nightmare to manage".
2025-05-12 08:19:54 -06:00
Peter Johnson
dd6c830768 Revert "Added parity with C++ units. (#7965)" (#7968)
This reverts commit 3dee19a435.

This was merged without sufficient review or discussion as to whether these units are value-add for the Java units library.
2025-05-11 15:08:31 -07:00
T Grinch
3dee19a435 Added parity with C++ units. (#7965)
* Added parity with C++ units.

* Update Units.java

* Update Units.java

* Formatting

* Fixed formatting

---------

Co-authored-by: thenetworkgrinch <thenetworkgrinch@users.noreply.github.com>
2025-05-10 21:04:17 -04:00
sciencewhiz
55a97f0c11 [examples] Add Analog Potentiometer Snippets (#7957) 2025-05-10 07:18:19 -07:00
Yuhao
b1f7e6d6f2 [cscore] Resolve macOS camera freeze with specific devices (#7960)
Addresses an issue where certain USB cameras, specifically the ArduCam OV9281, would freeze when attempting to stream on macOS.

The previous logic started the AVCaptureSession (startRunning) before locking the device for configuration (lockForConfiguration). While this works for many cameras, it causes the OV9281 to become unresponsive.

Further investigation revealed:
- Moving startRunning to after unlockForConfiguration resulted in macOS overriding the custom format and frame rate settings applied within the lock.
- The reliable solution, inspired by findings shared in the community (e.g., Stack Overflow), is to lock the device, apply the configuration, start the session, and then unlock the device.

This commit reorders the operations within deviceStreamOn in UsbCameraImplObjc.mm to follow the sequence: lockForConfiguration -> apply settings -> startRunning -> unlockForConfiguration. This ensures the desired camera configuration is applied correctly without causing device freezes on problematic hardware like the OV9281.
2025-05-07 19:57:03 -07:00
Gold856
ca3137b291 [build] Remove statically linked JNI artifacts (#7553) 2025-05-05 22:10:07 -07:00
Lucien Morey
e1da917270 [wpimath] Add dynamic size support for numerical jacobian computation (#7959) 2025-05-05 21:55:51 -07:00
sciencewhiz
b96264f042 [examples] Misc fixes for snippets (#7952) 2025-05-04 00:13:46 -07:00
sciencewhiz
a15152712f [examples] Add AnalogInput snippets (#7951) 2025-05-04 00:13:20 -07:00
Eric
17cae787e7 [commands] Mark CommandPtr class as [[nodiscard]] (#7803)
This has the same effect but makes it so any user code returning CommandPtr can't discard a returned command.

Signed-off-by: Eric Ward <ezeward4@gmail.com>
2025-05-03 20:44:55 -07:00
Jade
02de5f710e [cscore] Fix memory leak in usbviewer example (#7922)
Signed-off-by: Jade Turner <spacey-sooty@proton.me>
2025-05-01 22:07:45 -07:00
sciencewhiz
e63899e63a [examples] Add snippets for Digital Input article (#7949) 2025-05-01 11:40:38 -06:00
Tyler Veness
ba37e7eb3c [wpimath] Fix warning false positive from GCC 15 (#7948)
```
In file included from /usr/include/c++/15.1.1/functional:61,
                 from /home/tav/frc/wpilib/allwpilib/thirdparty/googletest/include/gtest/gtest-matchers.h:43,
                 from /home/tav/frc/wpilib/allwpilib/thirdparty/googletest/include/gtest/internal/gtest-death-test-internal.h:47,
                 from /home/tav/frc/wpilib/allwpilib/thirdparty/googletest/include/gtest/gtest-death-test.h:43,
                 from /home/tav/frc/wpilib/allwpilib/thirdparty/googletest/include/gtest/gtest.h:65,
                 from /home/tav/frc/wpilib/allwpilib/wpimath/src/test/native/cpp/controller/ControlAffinePlantInversionFeedforwardTest.cpp:7:
In copy constructor ‘std::function<_Res(_ArgTypes ...)>::function(const std::function<_Res(_ArgTypes ...)>&) [with _Res = Eigen::Matrix<double, 2, 1>; _ArgTypes = {const Eigen::Matrix<double, 2, 1, 0, 2, 1>&, const Eigen::Matrix<double, 1, 1, 0, 1, 1>&}]’,
    inlined from ‘frc::ControlAffinePlantInversionFeedforward<States, Inputs>::ControlAffinePlantInversionFeedforward(std::function<Eigen::Matrix<double, Size, 1, (Eigen::AutoAlign | (((Size == 1) && (1 != 1)) ? Eigen::RowMajor : (((1 == 1) && (Size != 1)) ? Eigen::ColMajor :  Eigen::ColMajor))), Size, 1>(const Eigen::Matrix<double, Size, 1, (Eigen::AutoAlign | (((Size == 1) && (1 != 1)) ? Eigen::RowMajor : (((1 == 1) && (Size != 1)) ? Eigen::ColMajor :  Eigen::ColMajor))), Size, 1>&, const Eigen::Matrix<double, Cols, 1, (Eigen::AutoAlign | (((Cols == 1) && (1 != 1)) ? Eigen::RowMajor : (((1 == 1) && (Cols != 1)) ? Eigen::ColMajor :  Eigen::ColMajor))), Cols, 1>&)>, units::time::second_t) [with int States = 2; int Inputs = 1]’ at /home/tav/frc/wpilib/allwpilib/wpimath/src/main/native/include/frc/controller/ControlAffinePlantInversionFeedforward.h:59:19,
    inlined from ‘virtual void frc::ControlAffinePlantInversionFeedforwardTest_Calculate_Test::TestBody()’ at /home/tav/frc/wpilib/allwpilib/wpimath/src/test/native/cpp/controller/ControlAffinePlantInversionFeedforwardTest.cpp:29:70:
/usr/include/c++/15.1.1/bits/std_function.h:393:17: error: ‘<anonymous>’ may be used uninitialized [-Werror=maybe-uninitialized]
  393 |             __x._M_manager(_M_functor, __x._M_functor, __clone_functor);
      |             ~~~~^~~~~~~~~~
/usr/include/c++/15.1.1/bits/std_function.h: In member function ‘virtual void frc::ControlAffinePlantInversionFeedforwardTest_Calculate_Test::TestBody()’:
/usr/include/c++/15.1.1/bits/std_function.h:269:7: note: by argument 2 of type ‘const std::_Any_data&’ to ‘static bool std::_Function_handler<_Res(_ArgTypes ...), _Functor>::_M_manager(std::_Any_data&, const std::_Any_data&, std::_Manager_operation) [with _Res = Eigen::Matrix<double, 2, 1>; _Functor = frc::ControlAffinePlantInversionFeedforwardTest_Calculate_Test::TestBody()::<lambda(auto:45&, auto:46&)>; _ArgTypes = {const Eigen::Matrix<double, 2, 1, 0, 2, 1>&, const Eigen::Matrix<double, 1, 1, 0, 1, 1>&}]’ declared here
  269 |       _M_manager(_Any_data& __dest, const _Any_data& __source,
      |       ^~~~~~~~~~
/home/tav/frc/wpilib/allwpilib/wpimath/src/test/native/cpp/controller/ControlAffinePlantInversionFeedforwardTest.cpp:29:70: note: ‘<anonymous>’ declared here
   29 |                                                                 20_ms};
      |                                                                      ^
In copy constructor ‘std::function<_Res(_ArgTypes ...)>::function(const std::function<_Res(_ArgTypes ...)>&) [with _Res = Eigen::Matrix<double, 2, 1>; _ArgTypes = {const Eigen::Matrix<double, 2, 1, 0, 2, 1>&}]’,
    inlined from ‘frc::ControlAffinePlantInversionFeedforward<States, Inputs>::ControlAffinePlantInversionFeedforward(std::function<Eigen::Matrix<double, Size, 1, (Eigen::AutoAlign | (((Size == 1) && (1 != 1)) ? Eigen::RowMajor : (((1 == 1) && (Size != 1)) ? Eigen::ColMajor :  Eigen::ColMajor))), Size, 1>(const Eigen::Matrix<double, Size, 1, (Eigen::AutoAlign | (((Size == 1) && (1 != 1)) ? Eigen::RowMajor : (((1 == 1) && (Size != 1)) ? Eigen::ColMajor :  Eigen::ColMajor))), Size, 1>&)>, frc::Matrixd<Rows, Cols>&, units::time::second_t) [with int States = 2; int Inputs = 1]’ at /home/tav/frc/wpilib/allwpilib/wpimath/src/main/native/include/frc/controller/ControlAffinePlantInversionFeedforward.h:79:11,
    inlined from ‘virtual void frc::ControlAffinePlantInversionFeedforwardTest_CalculateState_Test::TestBody()’ at /home/tav/frc/wpilib/allwpilib/wpimath/src/test/native/cpp/controller/ControlAffinePlantInversionFeedforwardTest.cpp:45:73:
/usr/include/c++/15.1.1/bits/std_function.h:393:17: error: ‘<anonymous>’ may be used uninitialized [-Werror=maybe-uninitialized]
  393 |             __x._M_manager(_M_functor, __x._M_functor, __clone_functor);
      |             ~~~~^~~~~~~~~~
/usr/include/c++/15.1.1/bits/std_function.h: In member function ‘virtual void frc::ControlAffinePlantInversionFeedforwardTest_CalculateState_Test::TestBody()’:
/usr/include/c++/15.1.1/bits/std_function.h:269:7: note: by argument 2 of type ‘const std::_Any_data&’ to ‘static bool std::_Function_handler<_Res(_ArgTypes ...), _Functor>::_M_manager(std::_Any_data&, const std::_Any_data&, std::_Manager_operation) [with _Res = Eigen::Matrix<double, 2, 1>; _Functor = frc::ControlAffinePlantInversionFeedforwardTest_CalculateState_Test::TestBody()::<lambda(auto:47&)>; _ArgTypes = {const Eigen::Matrix<double, 2, 1, 0, 2, 1>&}]’ declared here
  269 |       _M_manager(_Any_data& __dest, const _Any_data& __source,
      |       ^~~~~~~~~~
/home/tav/frc/wpilib/allwpilib/wpimath/src/test/native/cpp/controller/ControlAffinePlantInversionFeedforwardTest.cpp:45:73: note: ‘<anonymous>’ declared here
   45 |                                                                 B, 20_ms};
      |                                                                         ^
```
2025-05-01 11:40:13 -06:00
Cory
7036bd509e [wpimath] Correct mil unit (#7950) 2025-05-01 11:28:02 -06:00
Yuhao
8fe3cfb325 [cscore] Add UVC Protocol Support for USB Camera Controls on macOS (#7926) 2025-04-30 08:02:59 -06:00
Jonah Snider
a4572a01b7 [wpiutil] Add nested struct schemas before parent schema (#7935)
When adding struct schemas, the current logic is to add the parent/outer schema, and then add the schemas for any nested inner schemas.  This reverses that order to make it easier for tools to process.

Matches C++ logic.
2025-04-29 20:52:27 -07:00
Lucien Morey
f14af97dc7 [wpimath] Support dynamic matrix sizes in StateSpaceUtil (#7942) 2025-04-29 11:58:28 -06:00
Jade
631521a980 [thirdparty] Set IMGUI_DEFINE_MATH_OPERATORS (#7899)
This avoids the need to define it early in the include order everywhere else.

Signed-off-by: Jade Turner <spacey-sooty@proton.me>
2025-04-29 08:44:17 -06:00
sciencewhiz
c49fc29046 [examples] Add Limit Switch snippets (#7946) 2025-04-29 08:42:32 -06:00
Nolan Barker
40ce42712f [wpimath] Fix coordinate frame docs in HolonomicDriveController (#7938) 2025-04-28 16:27:38 -07:00
sciencewhiz
659710a79a [examples] Add remaining Encoder snippets (#7936) 2025-04-28 16:25:49 -07:00
David Vo
d059cbc157 [bazel] Hide wpinet implementation headers (#7941)
The headers in wpinet/src/main/native/cpp/*.h aren't intended to be used by dependents, so they shouldn't get propagated.
2025-04-28 16:46:22 -06:00
crueter
0695a4db89 [wpimath] Fix Debouncer type-changing behavior (#7870)
Closes #7867

Properly resets the baseline upon switching the debounce type, and adds
a test for such.

Signed-off-by: swurl <swurl@swurl.xyz>
2025-04-25 22:05:31 -07:00
me-it-is
3960045663 [wpimath] TrapezoidProfile: Fix null pointer when calling timeLeftUntil (#7894) 2025-04-25 21:58:05 -07:00
Tyler Veness
92010c175f Fix more emscripten compiler errors (#7895)
I ran the CMake configure with:
```bash
emcmake cmake -B build-wasm -S . \
  -DCMAKE_BUILD_TYPE=Release \
  -DBUILD_SHARED_LIBS=OFF \
  -DWITH_SIMULATION_MODULES=OFF \
  -DWITH_PROTOBUF=OFF \
  -DWITH_GUI=OFF \
  -DWITH_CSCORE=OFF
```

* Turned off simulation modules because they require shared libraries
* Turned off GUI because glfw requires libssh
* Turned off cscore because it requires OpenCV

I still get the following compiler errors:

```
/home/tav/frc/wpilib/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/linux.cpp:43:10: fatal error: 'sys/epoll.h' file not found
   43 | #include <sys/epoll.h>
      |          ^~~~~~~~~~~~~
```
```
/home/tav/frc/wpilib/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/stream.cpp:991:56: error: comparison of integers of different signs: 'unsigned long' and 'long' [-Werror,-Wsign-compare]
  991 |   for (cmsg = CMSG_FIRSTHDR(msg); cmsg != NULL; cmsg = CMSG_NXTHDR(msg, cmsg)) {
      |                                                        ^~~~~~~~~~~~~~~~~~~~~~
/home/tav/.cache/emscripten/sysroot/include/sys/socket.h:358:44: note: expanded from macro 'CMSG_NXTHDR'
  358 |         __CMSG_LEN(cmsg) + sizeof(struct cmsghdr) >= __MHDR_END(mhdr) - (unsigned char *)(cmsg) \
      |         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ^  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
```
```
/home/tav/frc/wpilib/allwpilib/wpinet/src/main/native/thirdparty/libuv/src/unix/core.cpp:748:56: error: comparison of integers of different signs: 'unsigned long' and 'long' [-Werror,-Wsign-compare]
  748 |   for (cmsg = CMSG_FIRSTHDR(msg); cmsg != NULL; cmsg = CMSG_NXTHDR(msg, cmsg))
      |                                                        ^~~~~~~~~~~~~~~~~~~~~~
/home/tav/.cache/emscripten/sysroot/include/sys/socket.h:358:44: note: expanded from macro 'CMSG_NXTHDR'
  358 |         __CMSG_LEN(cmsg) + sizeof(struct cmsghdr) >= __MHDR_END(mhdr) - (unsigned char *)(cmsg) \
      |         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ^  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
```
2025-04-25 21:56:26 -07:00
sciencewhiz
90ee11a9e0 [examples] Add DutyCycleEncoder Snippets (#7930) 2025-04-25 21:47:53 -07:00
David Vo
828199befb [ci] Use Bazel fastbuild compilation mode on Windows (#7925) 2025-04-25 21:46:40 -07:00
David Vo
d04e15b957 [bazel] Avoid globally linking macOS SDK frameworks (#7927) 2025-04-25 10:37:42 -07:00
Joseph Eng
d98ad815b1 [wpimath] Adjust applyDeadband logic (#7816)
Reduces nesting by returning when the value is within the deadband.

Adjusts the algorithm to handle large values of maxMagnitude naturally (instead of needing a separate check).

Reformats the math comments.
2025-04-23 08:41:03 -06:00
Austin Schuh
1c35a3a5ff [bazel] Make bazel work outside // (#7918)
The .bazelrc was doing a relative import, not an absolute one.

Signed-off-by: Austin Schuh <austin.linux@gmail.com>
2025-04-23 08:31:43 -06:00
Gold856
b248663386 [ci] Upgrade sccache action (#7914) 2025-04-22 15:27:55 -06:00
Gold856
9c60da1319 [ci] Use sccache for RobotPy build (#7915)
Also uses Artifactory to store the caches.
2025-04-22 15:27:20 -06:00
sciencewhiz
21d921184a [examples] Add compilable code snippets (#7909)
This enables frc-docs to use RLIs for things that are currently in-line
code blocks, and ensures they compile, which is important with the 2027
breaking changes coming. They are kept separate from the examples to
ensure they don't polute the VSCode examples finder.

Adds the Encoder snippets used in the frc-docs Encoder article as the
first instance of this.
2025-04-22 15:26:26 -06:00
Yuhao
26e299115f [cscore] Fix USB video mode handling on macOS (#7904)
* fix: UsbCameraImpl on macOS

* fix: add back logs
2025-04-21 09:27:42 -07:00
Wispy
07192285f6 [wpiunits] Add InchesPerSecondPerSecond (#7905) 2025-04-17 19:00:14 -07:00
Tyler Veness
ed94e3af3e [ci] Upgrade sccache action (#7911) 2025-04-17 18:59:10 -07:00
Tyler Veness
3cd282d9b0 [ci] Upgrade to wpiformat 2025.33 (#7902) 2025-04-12 20:29:09 -07:00
Tyler Veness
49b4b064cf [docs] Update readme to say Xcode is required (#7892)
The command line tools are insufficient.
2025-04-08 08:11:15 -06:00
Tyler Veness
bd78215b43 [glass] Fix compilation error with protobuf 30.1 (#7890)
```
/home/tav/frc/wpilib/allwpilib/glass/src/libnt/native/cpp/NetworkTables.cpp: In function ‘void UpdateProtobufValueSource(glass::NetworkTablesModel&, glass::NetworkTablesModel::ValueSource*, const google::protobuf::Message&, std::string_view, int64_t)’:
/home/tav/frc/wpilib/allwpilib/glass/src/libnt/native/cpp/NetworkTables.cpp:363:27: error: no match for ‘operator+’ (operand types are ‘const char [7]’ and ‘google::protobuf::internal::DescriptorStringView’ {aka ‘std::basic_string_view<char>’})
  363 |   out->typeStr = "proto:" + desc->full_name();
      |                  ~~~~~~~~ ^ ~~~~~~~~~~~~~~~~~
      |                  |                         |
      |                  const char [7]            google::protobuf::internal::DescriptorStringView {aka std::basic_string_view<char>}
```
2025-04-07 07:31:31 -06:00
Tyler Veness
c13b2f45c1 [wpimath] Update link to Ramsete controller derivation (#7887)
The derivation was moved from the Controls Engineering in FRC book to a
separate paper.
2025-03-30 16:25:08 -07:00
Jonah Snider
ea986427aa [wpiutil] Add Kaitai Struct definition for data log format (#7882)
Adds a Kaitai Struct definition for the WPILOG format. This can be used to generate code to read/write data log files without needing to build out a parser/serializer by hand. Or just an additional resource for readers to help understand the WPILOG format.
2025-03-27 16:51:13 -06:00
jpokornyiii
dcd397e007 [xrp] Update XRP for new SparkFun RP2350 Board (#7880)
Enables servos 3 and 4 usage on the new XRP board.
2025-03-26 08:50:09 -06:00
Jack Cammarata
1efaaefd78 [wpimath] Fix UnscentedKalmanFilter and improve math docs (#7850)
Throughout the code the state sqrt covariance S and innovation covariance Sy are maintained as upper triangular cholesky factors of those covariance matrices. The original paper defines P=S*S', so S should be lower triangular. The functions in the paper reflect this. In the code implementation, the sqrt covariance matrices are upper triangular, but the algorithm expects them to be lower triangular.

This bug was likely missed because the incorrect version of the filter is able to converge for some systems where all the states are observed, and the test case is set up such that all states are observed.

To fix the bug, a couple things needed to be changed:
all instances of rankUpdate() needed to be changed to use the lower triangular cholesky factor,
In the unscented transform, when S is found via QR decomposition, we need to take the transpose because R is upper triangular,
P() and SetP() functions need to be modified to be P=S*S' instead of P=S'*S, and P.llt().matrixL() instead of P.llt().matrixU() respectively.

Each part of the algorithm has also had the comments changed to clarify exactly which equation from the paper it implements.

Co-authored-by: Tyler Veness <calcmogul@gmail.com>
2025-03-14 10:05:15 -07:00
Tyler Veness
71b6e8ec58 [wpiutil] Avoid including execinfo.h for Emscripten target (#7854) 2025-03-06 23:15:22 -08:00
Tyler Veness
1a835fec01 [wpimath] Fix singularities in Ellipse2d::Nearest() (#7851)
The problem was ill-conditioned if either semiaxis had zero length.
2025-03-04 18:52:17 -08:00
crueter
0ad595c33c [wpimath] Add Debouncer type and time setters (#7839)
Signed-off-by: swurl <swurl@swurl.xyz>
Co-authored-by: Tyler Veness <calcmogul@gmail.com>
2025-03-03 17:21:38 -07:00
Thad House
93bf6c70ba [build] Remove sources from ceres-cpp (#7844) 2025-03-02 10:46:50 -08:00
arbessette
a7ae22d764 [docs] Update code of conduct (#7833) 2025-02-27 11:02:13 -08:00
Tyler Veness
822457d45b [wpimath] Fix feedforward returning NaN when kᵥ = 0 (#7790) 2025-02-25 19:07:51 -08:00
Adrien Bourdeaux
75321f1d84 [wpimath] Add Translation2d/Translation3d slew rate limiter (#7806)
Co-authored-by: Tyler Veness <calcmogul@gmail.com>
Co-authored-by: Joseph Eng <91924258+KangarooKoala@users.noreply.github.com>
2025-02-25 19:06:00 -08:00
Tyler Veness
cd6fee7fea [sysid] Refactor feedback analysis (#7827) 2025-02-25 19:05:05 -08:00
Tyler Veness
517344fe80 [wpimath] Fix another infinite loop in ArmFeedforward (#7823) 2025-02-25 19:04:31 -08:00
Rain Heuer
b0e588fd49 [glass] Update Field2D default field to 2025 (#7820) 2025-02-25 19:04:16 -08:00
Tyler Veness
0f0e93722e [ci] Upgrade to Clang 17 sanitizers (#7819) 2025-02-25 19:01:41 -08:00
sciencewhiz
f9307de04c [docs] Document that /format is disabled (#7810)
Add instructions for manual workarounds
2025-02-20 20:13:09 -08:00
sciencewhiz
15dcdebe21 [docs] Update contributing so breaking changes go to 2027 (#7809) 2025-02-20 20:12:48 -08:00
Tyler Veness
0c3c2c1fda [sysid] Remove extra period from exception messages (#7805) 2025-02-19 21:08:39 -08:00
HarryXChen
c898853b4d [wpilib] LinearSystemSim.setState: calculate new output state (#7799)
Establishes the invariant that the state and measurement always match up, even immediately after construction.
2025-02-18 22:49:28 -08:00
Sam Carlberg
bd2211119f [epilogue] Make nonloggable type warnings configurable (#7792)
Now a flag at the class level controls whether warning messages are printed.

Defaults to false (no warning messages).
2025-02-18 21:48:51 -08:00
Tyler Veness
13626063dc [wpimath] Fix units typo (#7789) 2025-02-13 23:22:44 -08:00
Peter Johnson
7e6077c546 [wpimath] Fix up order and docs for Feedforward gain setters (#7788) 2025-02-13 23:20:08 -08:00
Jade
4d126b158c [wpimath] Add setters to Feedforward gains (#7784)
Signed-off-by: Jade Turner <spacey-sooty@proton.me>
2025-02-13 21:09:28 -08:00
Peter Johnson
e22f76ce73 [build] Bump native-utils to 2025.9.1 (#7783) 2025-02-13 20:23:07 -08:00
Jonah Bonner
e648b9c86d [wpinet] Serve index HTML file from WebServer if available (#7780) 2025-02-13 18:10:02 -08:00
Kevin-OConnor
23658a8c62 [apriltag] Split 2025 AprilTag Maps (#7781)
Splits maps for welded vs AndyMark field perimeters. More info about why and what fields are at what events will be in TU12.
2025-02-13 17:53:11 -08:00
Tyler Veness
155b3d45e7 [wpiutil] Remove broken StackWalker library (#7777)
A Discord user reported that StackWalker gives blank stacktraces.

MSVC's C++23 support is unstable, so we can't use std::stacktrace yet.
In the meantime, we can just return an empty string and remove the
unmaintained StackWalker library and its hacky upstream_utils script.
2025-02-12 22:54:41 -08:00
Austin Schuh
d62ab12855 [bazel] Add missing -ldl when building libuv in bazel (#7775)
I was getting:
external/arm_frc_linux_gnueabi_repo/bin/../arm-nilrt-linux-gnueabi/sysroot/usr/lib/gcc/arm-nilrt-linux-gnueabi/12/../../../../../../../arm-nilrt-linux-gnueabi/bin/ld: bazel-out/k8-opt--roborio/bin/external/com_github_wpilibsuite_allwpilib/wpinet/libwpinet.static.a(fs.o): undefined reference to symbol 'dlsym@@GLIBC_2.4'
external/arm_frc_linux_gnueabi_repo/bin/../arm-nilrt-linux-gnueabi/sysroot/usr/lib/gcc/arm-nilrt-linux-gnueabi/12/../../../../../../../arm-nilrt-linux-gnueabi/bin/ld: external/arm_frc_linux_gnueabi_repo/bin/../arm-nilrt-linux-gnueabi/sysroot/lib/libdl.so.2: error adding symbols: DSO missing from command line
collect2: error: ld returned 1 exit status

This fixes that error for me.

Signed-off-by: Austin Schuh <austin.linux@gmail.com>
2025-02-12 16:17:11 -07:00
Dustin Spicuzza
1921d7b79a [wpilibc] Remove Alert magic static with map lookup (#7712)
The magic static adds yet another thing that needs to be reset if you
want to run a unit test with a completely reset wpilib state. Use the
existing Sendable static map to store the data instead.

This leaks memory if ResetSmartDashboardInstance is called.
2025-02-11 22:05:22 -08:00
Gold856
9dbb0c8c3d [ci] Turn on sync labels for the labeler (#7770)
This means PRs that had labels for certain changes will have those labels removed if those changes are removed.
2025-02-09 23:04:36 -08:00
Michael Fisher
b2584c6bdf [cmake] Use binary output dir for generated field images code (#7772) 2025-02-09 23:03:15 -08:00
Peter Johnson
53df127535 [cmake] Suppress enum warning on all clang, not just Apple (#7771) 2025-02-09 23:01:51 -08:00
Peter Johnson
d2611d4ad5 [hal] SPI: Remove byte limit on size in Java API (#7774)
The underlying Linux spidev supports up to page size length.
2025-02-09 23:01:01 -08:00
Ryan Blue
b60b2b64bd [hal, wpilib] AddressableLED: add support for other color orders (#7102)
Many LED strips use different color order (GRB in particular is common).

This makes the change at the HAL level. This solves 2 problems; first, no code needs to change in the high level drivers, which was challenging for C++, and second, simulation will behave properly as no conversion is needed. The HAL will accept an array of data objects in the same order no matter what the selected output order is, and will convert before sending it to the FPGA for output.

To accomplish this, NEON bulk load/interleave instructions are utilized. The low level implementation (load, store, and alignment functions) come from the Simd Library. The high level implementations are inspired by the image conversion functions in the simd library, but have diverged significantly.

Much of the implementation uses templates and inlined functions rather than runtime parameters; This is a trade off between the size of the generated code and the amount of function calls done at runtime. Currently, the entire conversion operation is inlined.
2025-02-07 13:36:41 -07:00
Ryan Blue
a0976a1fd9 [build] Update developerRobot JRE (#7764) 2025-02-07 11:24:30 -07:00
DeltaDizzy
b3b515e1dd [sysid] Error on missing tests in loaded DataLog (#7747)
* add exception

* detect missing tests

* throw in analyzer

* define tests setter

* change to std::map

* alignas fail

* make missingTests parameter const&

* const& impl

* use set and naive comparison

* use default comparison

* add <utility>

* Revert "alignas fail"

This reverts commit 5e97940f34.

* indent validtests

* format
2025-02-04 18:12:00 -05:00
Joseph Eng
296986397b [wpimath] Document drift from desaturating discretized chassis speeds (NFC) (#7741) 2025-02-03 11:46:18 -07:00
Sam Carlberg
18321e5551 [epilogue] Fix epilogue with package-info files (#7749) 2025-01-30 13:34:51 -07:00
Tyler Veness
b31fd17d05 [wpimath] Fix infinite loop in ArmFeedforward::Calculate(xₖ, vₖ, vₖ₊₁) (#7745)
Small values of kₐ make the iterative solver ill-conditioned. This
change reverts to the constant-acceleration feedforward in that case. It
gives _very_ bad results (hence why we added the iterative solver in the
first place), but it's better than hanging.

```
TEST(ArmFeedforwardTest, CalculateIllConditioned) {
  constexpr auto Ks = 0.5_V;
  constexpr auto Kv = 20_V / 1_rad_per_s;
  constexpr auto Ka = 1e-2_V / 1_rad_per_s_sq;
  constexpr auto Kg = 0_V;
  frc::ArmFeedforward armFF{Ks, Kg, Kv, Ka};

  // Calculate(currentAngle, currentVelocity, nextAngle, dt)
  CalculateAndSimulate(armFF, 0_rad, 0_rad_per_s, 2_rad_per_s, 20_ms);
}
```
This produces 1 V and doesn't accelerate the system at all. Using
nextVelocity instead of currentVelocity in the feedforward outputs 41 V
and still only accelerates to 0.4 rad/s of the requested 2 rad/s.

I picked the kₐ cutoff by increasing kₐ until the iterative solver
started converging.

Fixes #7743.
2025-01-30 13:33:39 -07:00
Gold856
b44a80c07a [build] cmake: Install wpimath nanopb headers (#7731) 2025-01-24 23:26:09 -08:00
Gold856
25d11524e8 [ci] Re-enable Artifactory cleaner cron job (#7721)
The query now targets the local repo for extra safety.
2025-01-23 21:46:13 -08:00
Peter Johnson
d86a2ec83b [ci] Fix labeler indentation (#7716) 2025-01-21 12:51:48 -07:00
sciencewhiz
0690d3d832 [ci] Update labeler for wpical and usage reporting (#7710) 2025-01-20 09:14:27 -07:00
Ryan Blue
304b98c0c9 [wpilibc] Alert: Fix first alert in group not publishing data (#7711) 2025-01-20 09:10:03 -07:00
Joseph Eng
72541c10e6 [wpilib, commands] Improve HID direction documentation (NFC) (#7672) 2025-01-19 20:34:07 -08:00
sciencewhiz
00445f4f27 [hal] Add Kitbot framework usage reporting (#7709)
Used in FIRST's kitbot code
2025-01-18 15:02:41 -08:00
Matthew Wozniak
8fc3767b30 [wpimath] Fix macro name typo (#7707) 2025-01-17 22:19:21 -08:00
Peter Johnson
5ab0409c15 [wpilib] ADIS164xx: report product ID on mismatch (#7706) 2025-01-17 18:14:20 -08:00
Peter Johnson
4caa16e254 [wpilibj] ADIS16470: Allow product ID of 16470 (#7704)
C++ allows either 16982 or 16470, do the same for Java.
2025-01-17 13:53:20 -08:00
Sam Carlberg
e52f400687 [wpiunits] Add Measure.per overloads for all known unit types (#7699)
Instead of only providing per(TimeUnit)

Useful for making conversion factors easier, eg `Inches.of(10).per(Rotation)` vs `Inches.of(10).per(Rotation.one())`

Update VelocityUnit.one() and VelocityUnit.zero() to return Velocity objects instead of generic Measure<? extends VelocityUnit<D>>; VelocityUnit is final, so the wildcard generic is unnecessary, and this makes the generated `per` functions possible for this type
2025-01-16 23:24:11 -08:00
PJ Reiniger
a9f3fc6b2c [bazel] Update toolchain to support systemcore (#7689) 2025-01-16 10:52:43 -07:00
David Racovan
a14545102f [wpimath] DifferentialDriveWheelPositions: tag as Proto/StructSerializable (#7622) 2025-01-13 14:52:40 -07:00
Tyler Veness
25e6549398 [wpimath] Fix various constexpr support bugs (#7676) 2025-01-13 14:44:55 -07:00
ハイドラント
cd92b07321 [wpimath] Add Pose2d and Pose3d rotateAround() (#7659) 2025-01-13 12:55:26 -07:00
Jason Daming
fc9e413ce1 [hal, wpilib] Add note about support for WS2815 (#7664) 2025-01-13 12:26:54 -07:00
Tyler Veness
007526089e [wpimath] Fix LinearSystemId return type and docs (#7675)
Fixes #7674.
2025-01-13 12:22:53 -07:00
Sam Carlberg
c5f7a2b4ac [epilogue] Fix lazy logging of mutable arrays (#7665) 2025-01-11 10:25:47 -08:00
1296 changed files with 51178 additions and 34907 deletions

View File

@@ -11,13 +11,19 @@ build --tool_java_runtime_version=remotejdk_17
test --test_output=errors
test --test_verbose_timeout_warnings
import shared/bazel/compiler_flags/sanitizers.rc
import shared/bazel/compiler_flags/base_linux_flags.rc
import shared/bazel/compiler_flags/linux_flags.rc
import shared/bazel/compiler_flags/osx_flags.rc
import shared/bazel/compiler_flags/roborio_flags.rc
import shared/bazel/compiler_flags/windows_flags.rc
import shared/bazel/compiler_flags/coverage_flags.rc
import %workspace%/shared/bazel/compiler_flags/sanitizers.rc
import %workspace%/shared/bazel/compiler_flags/base_linux_flags.rc
import %workspace%/shared/bazel/compiler_flags/linux_flags.rc
import %workspace%/shared/bazel/compiler_flags/osx_flags.rc
import %workspace%/shared/bazel/compiler_flags/roborio_flags.rc
import %workspace%/shared/bazel/compiler_flags/systemcore_flags.rc
import %workspace%/shared/bazel/compiler_flags/windows_flags.rc
import %workspace%/shared/bazel/compiler_flags/coverage_flags.rc
# Alias toolchain names to what wpilibsuite uses for CI/Artifact naming
build:athena --config=roborio
build:linuxarm32 --config=raspibookworm32
build:linuxarm64 --config=bookworm64
build:build_java --test_tag_filters=allwpilib-build-java --build_tag_filters=allwpilib-build-java
build:build_cpp --test_tag_filters=+allwpilib-build-cpp --build_tag_filters=+allwpilib-build-cpp

View File

@@ -45,6 +45,8 @@ Checks:
-clang-diagnostic-#warnings,
-clang-diagnostic-pedantic,
clang-analyzer-*,
-clang-analyzer-optin.cplusplus.UninitializedObject,
-clang-analyzer-security.FloatLoopCounter,
cppcoreguidelines-slicing,
google-build-namespaces,
google-explicit-constructor,

View File

@@ -1,6 +1,4 @@
color: false
definitions: [cmake/modules]
line_length: 100
list_expansion: favour-inlining
quiet: false
unsafe: false

9
.github/labeler.yml vendored
View File

@@ -54,3 +54,12 @@
'component: wpiutil':
- changed-files:
- any-glob-to-any-file: wpiutil/**
'component: wpical':
- changed-files:
- any-glob-to-any-file: wpical/**
'component: usage reporting':
- changed-files:
- any-glob-to-any-file: hal/src/generate/**
'attn: NI':
- changed-files:
- any-glob-to-any-file: hal/src/generate/**

View File

@@ -3,7 +3,10 @@
{
"aql": {
"items.find": {
"repo": "wpilib-mvn-development",
"$or":[
{ "repo": "wpilib-mvn-development-local" },
{ "repo": "wpilib-mvn-development-2027-local" }
],
"path": { "$nmatch":"*edu/wpi/first/thirdparty*" },
"$or":[
{

View File

@@ -2,6 +2,8 @@ name: Artifactory Nightly Cleanup
on:
workflow_dispatch:
schedule:
- cron: '15 2 * * *'
jobs:
wpilib-mvn-development_unused_cleanup:

View File

@@ -31,13 +31,13 @@ jobs:
with:
token: ${{ secrets.BUILDBUDDY_API_KEY }}
- name: Build Release
run: bazel --output_user_root=C:\\bazelroot ${{ matrix.action }} -k ... --config=ci -c opt ${{ matrix.config }} --verbose_failures
- name: bazel ${{ matrix.action }}
run: bazel --output_user_root=C:\\bazelroot ${{ matrix.action }} -k ... --config=ci ${{ matrix.config }} --verbose_failures
shell: bash
build-mac:
name: "Mac"
runs-on: macos-14
runs-on: macOS-15
steps:
- uses: actions/checkout@v4
with: { fetch-depth: 0 }
@@ -47,7 +47,7 @@ jobs:
with:
token: ${{ secrets.BUILDBUDDY_API_KEY }}
- name: Build Release
- name: bazel test (release)
run: bazel test -k ... --config=ci -c opt --config=macos --nojava_header_compilation --verbose_failures
shell: bash
@@ -70,7 +70,7 @@ jobs:
with:
token: ${{ secrets.BUILDBUDDY_API_KEY }}
- name: Build and Test Release
- name: bazel ${{ matrix.action }} (release)
run: bazel ${{ matrix.action }} ... --config=ci -c opt ${{ matrix.config }} -k --verbose_failures
buildifier:

View File

@@ -30,7 +30,7 @@ jobs:
- uses: nttld/setup-ndk@v1
id: setup-ndk
with:
ndk-version: r27c
ndk-version: r27d
add-to-path: false
- uses: actions/setup-java@v4
@@ -39,13 +39,19 @@ jobs:
java-version: 17
- name: Install sccache
uses: mozilla-actions/sccache-action@v0.0.5
uses: mozilla-actions/sccache-action@v0.0.9
- name: Install dependencies
run: sudo apt-get update && sudo apt-get install -y ninja-build
- name: configure
run: cmake --preset with-sccache -DCMAKE_BUILD_TYPE=RelWithDebInfo -DWITH_WPILIB=OFF -DWITH_GUI=OFF -DWITH_CSCORE=OFF -DWITH_TESTS=OFF -DWITH_SIMULATION_MODULES=OFF -DWITH_PROTOBUF=OFF -DWITH_JAVA=ON -DBUILD_SHARED_LIBS=ON -DCMAKE_TOOLCHAIN_FILE=${{ steps.setup-ndk.outputs.ndk-path }}/build/cmake/android.toolchain.cmake -DANDROID_ABI="${{ matrix.abi }}" -DANDROID_PLATFORM=android-24
env:
SCCACHE_WEBDAV_USERNAME: ${{ secrets.ARTIFACTORY_USERNAME }}
SCCACHE_WEBDAV_PASSWORD: ${{ secrets.ARTIFACTORY_PASSWORD }}
- name: build
run: cmake --build build-cmake --parallel $(nproc)
env:
SCCACHE_WEBDAV_USERNAME: ${{ secrets.ARTIFACTORY_USERNAME }}
SCCACHE_WEBDAV_PASSWORD: ${{ secrets.ARTIFACTORY_PASSWORD }}

View File

@@ -20,11 +20,11 @@ jobs:
name: Linux
container: wpilib/roborio-cross-ubuntu:2025-22.04
flags: "--preset with-java-and-sccache -DCMAKE_BUILD_TYPE=Release -DWITH_EXAMPLES=ON"
- os: macOS-14
- os: macOS-15
name: macOS
container: ""
env: "PATH=\"/opt/homebrew/opt/protobuf@3/bin:$PATH\""
flags: "--preset with-sccache -DCMAKE_BUILD_TYPE=Release -DWITH_EXAMPLES=ON -DCMAKE_LIBRARY_PATH=/opt/homebrew/opt/protobuf@3/lib -DProtobuf_INCLUDE_DIR=/opt/homebrew/opt/protobuf@3/include -DProtobuf_PROTOC_EXECUTABLE=/opt/homebrew/opt/protobuf@3/bin/protoc"
env: ""
flags: "--preset with-sccache -DCMAKE_BUILD_TYPE=Release -DWITH_EXAMPLES=ON"
- os: windows-2022
name: Windows
container: ""
@@ -40,7 +40,7 @@ jobs:
- name: Install dependencies (macOS)
if: runner.os == 'macOS'
run: brew install opencv protobuf@3 ninja
run: brew install opencv protobuf@29 ninja
- uses: ilammy/msvc-dev-cmd@v1.13.0
if: runner.os == 'Windows'
@@ -50,7 +50,7 @@ jobs:
uses: lukka/get-cmake@v3.29.3
- name: Install sccache
uses: mozilla-actions/sccache-action@v0.0.5
uses: mozilla-actions/sccache-action@v0.0.9
- uses: actions/checkout@v4
@@ -59,7 +59,7 @@ jobs:
uses: lukka/run-vcpkg@v11.5
with:
vcpkgDirectory: ${{ runner.workspace }}/vcpkg
vcpkgGitCommitId: 37c3e63a1306562f7f59c4c3c8892ddd50fdf992 # HEAD on 2024-02-24
vcpkgGitCommitId: 74e6536215718009aae747d86d84b78376bf9e09 # HEAD on 2025-10-17
- name: configure
run: cmake ${{ matrix.flags }}

View File

@@ -1,67 +0,0 @@
name: Documentation
on: [push, workflow_dispatch]
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
env:
BASE_PATH: allwpilib/docs
jobs:
publish:
name: "Documentation - Publish"
runs-on: ubuntu-22.04
if: github.repository == 'wpilibsuite/allwpilib' && (github.ref == 'refs/heads/main' || (startsWith(github.ref, 'refs/tags/v') && !contains(github.ref, '2027')))
concurrency: ci-docs-publish
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
persist-credentials: false
- uses: gradle/actions/wrapper-validation@v4
- uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: 17
- name: Set environment variables (Development)
run: |
echo "BRANCH=development" >> $GITHUB_ENV
if: github.ref == 'refs/heads/main'
- name: Set environment variables (Tag)
run: |
echo "EXTRA_GRADLE_ARGS=-PreleaseMode" >> $GITHUB_ENV
echo "BRANCH=beta" >> $GITHUB_ENV
if: startsWith(github.ref, 'refs/tags/v') && !contains(github.ref, '2027')
- name: Set environment variables (Release)
run: |
echo "EXTRA_GRADLE_ARGS=-PreleaseMode" >> $GITHUB_ENV
echo "BRANCH=release" >> $GITHUB_ENV
if: startsWith(github.ref, 'refs/tags/v') && !contains(github.ref, 'alpha') && !contains(github.ref, 'beta') && !contains(github.ref, '2027')
- name: Build with Gradle
run: ./gradlew docs:generateJavaDocs docs:doxygen -PbuildServer ${{ env.EXTRA_GRADLE_ARGS }}
- name: Install SSH Client 🔑
uses: webfactory/ssh-agent@v0.9.0
with:
ssh-private-key: ${{ secrets.GH_DEPLOY_KEY }}
- name: Deploy 🚀
uses: JamesIves/github-pages-deploy-action@v4.6.1
with:
ssh-key: true
repository-name: wpilibsuite/wpilibsuite.github.io
branch: allwpilib-${{ env.BRANCH }}
clean: true
single-commit: true
folder: docs/build/docs
- name: Trigger Workflow
uses: actions/github-script@v7
with:
github-token: ${{ secrets.DISPATCH_PAT_TOKEN }}
script: |
github.rest.actions.createWorkflowDispatch({
owner: context.repo.owner,
repo: 'wpilibsuite.github.io',
workflow_id: 'static.yml',
ref: 'main',
})

View File

@@ -31,6 +31,9 @@ def main():
# Replace GCC warning argument with one Clang recognizes
elif arg == "-Wno-maybe-uninitialized":
out_args.append("-Wno-uninitialized")
# Skip GCC-specific warning argument
elif arg == "-Wno-error=restrict":
pass
else:
out_args.append(arg)

View File

@@ -52,11 +52,11 @@ jobs:
run: echo "EXTRA_GRADLE_ARGS=-PreleaseMode" >> $GITHUB_ENV
if: startsWith(github.ref, 'refs/tags/v') && !contains(github.ref, '2027')
- name: Build with Gradle
uses: addnab/docker-run-action@v3
uses: wpilibsuite/docker-run-action@v4
with:
image: ${{ matrix.container }}
options: -v ${{ github.workspace }}:/work -w /work -e ARTIFACTORY_PUBLISH_USERNAME -e ARTIFACTORY_PUBLISH_PASSWORD -e GITHUB_REF -e CI
run: df . && rm -f semicolon_delimited_script && echo $GITHUB_REF && ./gradlew build --build-cache -PbuildServer -PskipJavaFormat ${{ matrix.build-options }} ${{ env.EXTRA_GRADLE_ARGS }}
run: df . && echo $GITHUB_REF && ./gradlew build --build-cache -PbuildServer -PskipJavaFormat ${{ matrix.build-options }} ${{ env.EXTRA_GRADLE_ARGS }}
env:
ARTIFACTORY_PUBLISH_USERNAME: ${{ secrets.ARTIFACTORY_USERNAME }}
ARTIFACTORY_PUBLISH_PASSWORD: ${{ secrets.ARTIFACTORY_PASSWORD }}
@@ -98,7 +98,7 @@ jobs:
build-options: "-PciReleaseOnly -Pbuildwinarm64 -Ponlywindowsarm64"
task: "copyAllOutputs"
outputs: "build/allOutputs"
- os: macOS-14
- os: macOS-15
artifact-name: macOS
architecture: aarch64
task: "build"
@@ -153,7 +153,7 @@ jobs:
if: matrix.os == 'windows-2022'
- name: Check disk free space pre-cleanup (macOS)
run: df -h .
if: matrix.os == 'macOS-14'
if: matrix.os == 'macOS-15'
- name: Cleanup disk space
# CodeQL: 5G
# go: 748M
@@ -162,10 +162,10 @@ jobs:
rm -rf /Users/runner/hostedtoolcache/CodeQL
rm -rf /Users/runner/hostedtoolcache/go
rm -rf /Users/runner/Library/Android
if: matrix.os == 'macOS-14'
if: matrix.os == 'macOS-15'
- name: Check disk free space post-cleanup (macOS)
run: df -h .
if: matrix.os == 'macOS-14'
if: matrix.os == 'macOS-15'
- name: Build with Gradle
run: ./gradlew ${{ matrix.task }} --build-cache -PbuildServer -PskipJavaFormat ${{ matrix.build-options }} ${{ env.EXTRA_GRADLE_ARGS }}
env:
@@ -181,7 +181,7 @@ jobs:
if: matrix.os == 'windows-2022'
- name: Check disk free space (macOS)
run: df -h .
if: matrix.os == 'macOS-14'
if: matrix.os == 'macOS-15'
- uses: actions/upload-artifact@v4
with:
name: ${{ matrix.artifact-name }}
@@ -203,7 +203,7 @@ jobs:
run: echo "EXTRA_GRADLE_ARGS=-PreleaseMode" >> $GITHUB_ENV
if: startsWith(github.ref, 'refs/tags/v') && !contains(github.ref, '2027')
- name: Build with Gradle
run: ./gradlew docs:zipDocs --build-cache -PbuildServer ${{ env.EXTRA_GRADLE_ARGS }}
run: ./gradlew docs:zipDocs --build-cache -PbuildServer -PdocWarningsAsErrors ${{ env.EXTRA_GRADLE_ARGS }}
env:
ARTIFACTORY_PUBLISH_USERNAME: ${{ secrets.ARTIFACTORY_USERNAME }}
ARTIFACTORY_PUBLISH_PASSWORD: ${{ secrets.ARTIFACTORY_PASSWORD }}
@@ -212,6 +212,68 @@ jobs:
name: Documentation
path: docs/build/outputs
publish:
name: "Documentation - Publish"
runs-on: ubuntu-22.04
if: github.repository == 'wpilibsuite/allwpilib' && (github.ref == 'refs/heads/main' || (startsWith(github.ref, 'refs/tags/v') && !contains(github.ref, '2027')))
needs: [build-documentation]
concurrency: ci-docs-publish
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
persist-credentials: false
- name: Download docs artifacts
uses: actions/download-artifact@v4
with:
name: Documentation
- name: Make output directories
run: |
mkdir -p docs/tmp/doxygen/html
mkdir -p docs/tmp/javadoc
- name: Extract docs
run: |
unzip _GROUP_edu_wpi_first_wpilibc_ID_documentation_CLS.zip -d docs/tmp/doxygen/html
unzip _GROUP_edu_wpi_first_wpilibj_ID_documentation_CLS.zip -d docs/tmp/javadoc
- name: Set environment variables (Development)
run: |
echo "BRANCH=development" >> $GITHUB_ENV
if: github.ref == 'refs/heads/main'
- name: Set environment variables (Tag)
run: |
echo "EXTRA_GRADLE_ARGS=-PreleaseMode" >> $GITHUB_ENV
echo "BRANCH=beta" >> $GITHUB_ENV
if: startsWith(github.ref, 'refs/tags/v') && !contains(github.ref, '2027')
- name: Set environment variables (Release)
run: |
echo "EXTRA_GRADLE_ARGS=-PreleaseMode" >> $GITHUB_ENV
echo "BRANCH=release" >> $GITHUB_ENV
if: startsWith(github.ref, 'refs/tags/v') && !contains(github.ref, 'alpha') && !contains(github.ref, 'beta') && !contains(github.ref, '2027')
- name: Install SSH Client 🔑
uses: webfactory/ssh-agent@v0.9.0
with:
ssh-private-key: ${{ secrets.GH_DEPLOY_KEY }}
- name: Deploy 🚀
uses: JamesIves/github-pages-deploy-action@v4.6.1
with:
ssh-key: true
repository-name: wpilibsuite/wpilibsuite.github.io
branch: allwpilib-${{ env.BRANCH }}
clean: true
single-commit: true
folder: docs/tmp
- name: Trigger Workflow
uses: actions/github-script@v7
with:
github-token: ${{ secrets.DISPATCH_PAT_TOKEN }}
script: |
github.rest.actions.createWorkflowDispatch({
owner: context.repo.owner,
repo: 'wpilibsuite.github.io',
workflow_id: 'static.yml',
ref: 'main',
})
combine:
name: Combine
needs: [build-docker, build-host, build-documentation]

View File

@@ -1,6 +1,6 @@
name: "Pull Request Labeler"
on:
- pull_request_target
- pull_request_target
jobs:
labeler:
@@ -9,4 +9,6 @@ jobs:
pull-requests: write
runs-on: ubuntu-latest
steps:
- uses: actions/labeler@v5
- uses: actions/labeler@v5
with:
sync-labels: true

View File

@@ -36,7 +36,7 @@ jobs:
- name: Install wpiformat
run: |
python -m venv ${{ runner.temp }}/wpiformat
${{ runner.temp }}/wpiformat/bin/pip3 install wpiformat==2024.51
${{ runner.temp }}/wpiformat/bin/pip3 install wpiformat==2025.36
- name: Run
run: ${{ runner.temp }}/wpiformat/bin/wpiformat
- name: Check output
@@ -78,7 +78,7 @@ jobs:
- name: Install wpiformat
run: |
python -m venv ${{ runner.temp }}/wpiformat
${{ runner.temp }}/wpiformat/bin/pip3 install wpiformat==2024.51
${{ runner.temp }}/wpiformat/bin/pip3 install wpiformat==2025.36
- name: Create compile_commands.json
run: |
./gradlew generateCompileCommands -Ptoolchain-optional-roboRio
@@ -123,18 +123,3 @@ jobs:
echo '' >> $GITHUB_STEP_SUMMARY
echo '```' >> $GITHUB_STEP_SUMMARY
if: ${{ failure() }}
documentation:
name: "Documentation"
runs-on: ubuntu-22.04
needs: [validation]
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: 21
- name: Build with Gradle
run: ./gradlew docs:zipDocs -PbuildServer -PdocWarningsAsErrors ${{ env.EXTRA_GRADLE_ARGS }}

View File

@@ -29,19 +29,19 @@ jobs:
ctest-env: ""
ctest-flags: ""
name: "${{ matrix.name }}"
runs-on: ubuntu-22.04
container: wpilib/roborio-cross-ubuntu:2025-22.04
runs-on: ubuntu-24.04
container: wpilib/roborio-cross-ubuntu:2025-24.04
steps:
- name: Install Dependencies
run: sudo apt-get update && sudo apt-get install -y libopencv-dev libopencv4.5-java clang-14 libprotobuf-dev protobuf-compiler ninja-build
run: sudo apt-get update && sudo apt-get install -y libopencv-dev libopencv-java clang-17 libprotobuf-dev protobuf-compiler ninja-build
- name: Install sccache
uses: mozilla-actions/sccache-action@v0.0.5
uses: mozilla-actions/sccache-action@v0.0.9
- uses: actions/checkout@v4
- name: configure
run: mkdir build && cd build && cmake -G Ninja -DCMAKE_C_COMPILER_LAUNCHER=sccache -DCMAKE_CXX_COMPILER_LAUNCHER=sccache -DCMAKE_C_COMPILER:FILEPATH=/usr/bin/clang-14 -DCMAKE_CXX_COMPILER:FILEPATH=/usr/bin/clang++-14 -DWITH_JAVA=OFF ${{ matrix.cmake-flags }} ..
run: mkdir build && cd build && cmake -G Ninja -DCMAKE_C_COMPILER_LAUNCHER=sccache -DCMAKE_CXX_COMPILER_LAUNCHER=sccache -DCMAKE_C_COMPILER:FILEPATH=/usr/bin/clang-17 -DCMAKE_CXX_COMPILER:FILEPATH=/usr/bin/clang++-17 -DWITH_JAVA=OFF ${{ matrix.cmake-flags }} ..
env:
SCCACHE_WEBDAV_USERNAME: ${{ secrets.ARTIFACTORY_USERNAME }}
SCCACHE_WEBDAV_PASSWORD: ${{ secrets.ARTIFACTORY_PASSWORD }}

View File

@@ -53,11 +53,11 @@ jobs:
with:
fetch-depth: 0
- name: Build with Gradle
uses: addnab/docker-run-action@v3
uses: wpilibsuite/docker-run-action@v4
with:
image: ${{ matrix.container }}
options: -v ${{ github.workspace }}:/work -w /work -e GITHUB_REF -e CI
run: df . && rm -f semicolon_delimited_script && echo $GITHUB_REF && ./gradlew build -PbuildServer -PskipJavaFormat ${{ matrix.build-options }}
run: df . && echo $GITHUB_REF && ./gradlew build -PbuildServer -PskipJavaFormat ${{ matrix.build-options }}
- name: Check free disk space
run: df .
- uses: actions/upload-artifact@v4
@@ -97,15 +97,22 @@ jobs:
build-options: "-PciReleaseOnly -Pbuildwinarm64 -Ponlywindowsarm64"
task: "copyAllOutputs"
outputs: "build/allOutputs"
- os: macOS-14
- os: macOS-15
artifact-name: macOS
architecture: aarch64
task: "build"
outputs: "build/allOutputs"
- os: windows-2022
artifact-name: Win32
artifact-name: Win32FFI
architecture: x86
task: ":ntcoreffi:build"
build-options: "-Pntcoreffibuild \"-Dorg.gradle.jvmargs=-Xmx1096m\""
outputs: "ntcoreffi/build/outputs"
- os: windows-2022
artifact-name: Win64FFI
architecture: x64
task: ":ntcoreffi:build"
build-options: "-Pntcoreffibuild -Pbuildwinarm64"
outputs: "ntcoreffi/build/outputs"
name: "Build - ${{ matrix.artifact-name }}"
runs-on: ${{ matrix.os }}
@@ -139,7 +146,7 @@ jobs:
if: matrix.os == 'windows-2022'
- name: Check disk free space pre-cleanup (macOS)
run: df -h .
if: matrix.os == 'macOS-14'
if: matrix.os == 'macOS-15'
- name: Cleanup disk space
# CodeQL: 5G
# go: 748M
@@ -148,10 +155,10 @@ jobs:
rm -rf /Users/runner/hostedtoolcache/CodeQL
rm -rf /Users/runner/hostedtoolcache/go
rm -rf /Users/runner/Library/Android
if: matrix.os == 'macOS-14'
if: matrix.os == 'macOS-15'
- name: Check disk free space post-cleanup (macOS)
run: df -h .
if: matrix.os == 'macOS-14'
if: matrix.os == 'macOS-15'
- name: Build with Gradle
run: ./gradlew ${{ matrix.task }} -PbuildServer -PskipJavaFormat ${{ matrix.build-options }}
- name: Sign Libraries with Developer ID
@@ -163,7 +170,7 @@ jobs:
if: matrix.os == 'windows-2022'
- name: Check disk free space (macOS)
run: df -h .
if: matrix.os == 'macOS-14'
if: matrix.os == 'macOS-15'
- uses: actions/upload-artifact@v4
with:
name: ${{ matrix.artifact-name }}

View File

@@ -7,7 +7,7 @@ concurrency:
cancel-in-progress: true
env:
YEAR: 2025
YEAR: 2026
jobs:
build-artifacts:
@@ -31,11 +31,11 @@ jobs:
fetch-depth: 0
- uses: gradle/actions/wrapper-validation@v4
- name: Build WPILib with Gradle
uses: addnab/docker-run-action@v3
uses: wpilibsuite/docker-run-action@v4
with:
image: wpilib/roborio-cross-ubuntu:2025-22.04
options: -v ${{ github.workspace }}:/work -w /work -e GITHUB_REF -e CI -e DISPLAY
run: df . && rm -f semicolon_delimited_script && ./gradlew :wpilibc:publish :wpilibj:publish :wpilibNewCommands:publish :hal:publish :cameraserver:publish :ntcore:publish :cscore:publish :wpimath:publish :wpinet:publish :wpiutil:publish :apriltag:publish :wpiunits:publish :simulation:halsim_gui:publish :simulation:halsim_ds_socket:publish :simulation:halsim_ws_server:publish :simulation:halsim_ws_client:publish :simulation:halsim_xrp:publish :fieldImages:publish :romiVendordep:publish :xrpVendordep:publish :epilogue-processor:publish :epilogue-runtime:publish :thirdparty:googletest:publish -x test -x Javadoc -x doxygen --build-cache && cp -r /root/releases/maven/development /work
run: df . && ./gradlew :wpilibc:publish :wpilibj:publish :wpilibNewCommands:publish :hal:publish :cameraserver:publish :ntcore:publish :cscore:publish :wpimath:publish :wpinet:publish :wpiutil:publish :apriltag:publish :wpiunits:publish :simulation:halsim_gui:publish :simulation:halsim_ds_socket:publish :simulation:halsim_ws_server:publish :simulation:halsim_ws_client:publish :simulation:halsim_xrp:publish :fieldImages:publish :romiVendordep:publish :xrpVendordep:publish :epilogue-processor:publish :epilogue-runtime:publish :thirdparty:googletest:publish -x test -x Javadoc -x doxygen --build-cache && cp -r /root/releases/maven/development /work
- uses: actions/upload-artifact@v4
with:
name: MavenArtifacts
@@ -150,57 +150,47 @@ jobs:
build/allOutputs/
retention-days: 7
Robotpy:
name: "Build - Robotpy"
needs: [build-artifacts]
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
with:
repository: robotpy/mostrobotpy
fetch-depth: 0
- uses: actions/download-artifact@v4
with:
name: MavenArtifacts
- name: Move artifacts
run: mkdir -p ~/releases/maven/development && cp -r edu ~/releases/maven/development
- uses: actions/setup-python@v5
with:
python-version: 3.13
#
# Setup build caching
#
- name: Set ccache size
shell: bash
id: ccache
run: echo "MAX_SIZE=500M" >> $GITHUB_OUTPUT
- name: Setup ccache
# uses: hendrikmuhs/ccache-action@v1.2.10
uses: robotpy/ccache-action@fork
with:
key: ubuntu-22.04-3.13
variant: ccache
max-size: ${{ steps.ccache.outputs.max_size }}
- name: Install deps
shell: bash
run: |
python -m pip --disable-pip-version-check install -r rdev_requirements.txt
- name: Install numpy (needed for stubgen but broken in raspbian CI)
shell: bash
run: |
python -m pip --disable-pip-version-check install numpy
- name: Patch RobotPy rdev to use local development
run: git config user.name github-actions && git config user.email github-actions@github.com && set -- ~/releases/maven/development/edu/wpi/first/wpiutil/wpiutil-cpp/*/ ; wpilibversion=$(basename $1) && echo $wpilibversion && sed --regexp-extended -i 's@(wpilib_bin_url =).*@\1 \"file:\/\/'"$HOME"'\/releases\/maven\/development"@' rdev.toml && sed --regexp-extended -i 's/(wpilib_bin_version =).*/\1 \"'"$wpilibversion"'\"/' rdev.toml && ./rdev.sh update-pyproject --commit
- name: Build + test wheels
shell: bash
run: |
./rdev.sh ci run
env:
RPYBUILD_STRIP_LIBPYTHON: "1"
RPYBUILD_CC_LAUNCHER: ccache
# Robotpy:
# name: "Build - Robotpy"
# needs: [build-artifacts]
# runs-on: ubuntu-22.04
# steps:
# - uses: actions/checkout@v4
# with:
# repository: robotpy/mostrobotpy
# fetch-depth: 0
# - uses: actions/download-artifact@v4
# with:
# name: MavenArtifacts
# - name: Move artifacts
# run: mkdir -p ~/releases/maven/development && cp -r edu ~/releases/maven/development
#
# - uses: actions/setup-python@v5
# with:
# python-version: 3.13
#
# - name: Install sccache
# uses: mozilla-actions/sccache-action@v0.0.9
#
# - name: Install deps
# shell: bash
# run: |
# python -m pip --disable-pip-version-check install -r rdev_requirements.txt
#
# - name: Install numpy (needed for stubgen but broken in raspbian CI)
# shell: bash
# run: |
# python -m pip --disable-pip-version-check install numpy
# - name: Patch RobotPy rdev to use local development
# run: git config user.name github-actions && git config user.email github-actions@github.com && set -- ~/releases/maven/development/edu/wpi/first/wpiutil/wpiutil-cpp/*/ ; wpilibversion=$(basename $1) && echo $wpilibversion && sed --regexp-extended -i 's@(wpilib_bin_url =).*@\1 \"file:\/\/'"$HOME"'\/releases\/maven\/development"@' rdev.toml && sed --regexp-extended -i 's/(wpilib_bin_version =).*/\1 \"'"$wpilibversion"'\"/' rdev.toml && ./rdev.sh update-pyproject --commit
# - name: Build + test wheels
# shell: bash
# run: |
# ./rdev.sh ci run
# env:
# RPYBUILD_STRIP_LIBPYTHON: "1"
# RPYBUILD_CC_LAUNCHER: sccache
# SCCACHE_WEBDAV_ENDPOINT: "https://frcmaven.wpi.edu/artifactory/wpilib-generic-cache-cmake-local"
# SCCACHE_WEBDAV_KEY_PREFIX: "sccache-robotpy"
# SCCACHE_WEBDAV_USERNAME: ${{ secrets.ARTIFACTORY_USERNAME }}
# SCCACHE_WEBDAV_PASSWORD: ${{ secrets.ARTIFACTORY_PASSWORD }}

View File

@@ -120,18 +120,6 @@ jobs:
./mpack.py clone
./mpack.py copy-src
./mpack.py format-patch
- name: Run stack_walker.py
run: |
cd upstream_utils
./stack_walker.py clone
./stack_walker.py copy-src
./stack_walker.py format-patch
- name: Run memory.py
run: |
cd upstream_utils
./memory.py clone
./memory.py copy-src
./memory.py format-patch
- name: Run protobuf.py
run: |
cd upstream_utils
@@ -153,4 +141,11 @@ jobs:
- name: Add untracked files to index so they count as changes
run: git add -A
- name: Check output
run: git --no-pager diff --exit-code HEAD ':!*.bazel'
run: |
set +e
git --no-pager diff --exit-code HEAD ':!*.bazel'
git_exit_code=$?
if test "$git_exit_code" -ne "0"; then
echo "::error ::upstream_utils check failed. This is usually caused by a bad script or the copied files differing from what the script outputs. You can learn more about using upstream_utils to modify thirdparty libraries at https://github.com/wpilibsuite/allwpilib/blob/main/upstream_utils/README.md"
exit $git_exit_code
fi

3
.gitignore vendored
View File

@@ -255,3 +255,6 @@ bazel_auth.rc
# ctest
/Testing/
# Meson
.meson-subproject*

View File

@@ -91,6 +91,7 @@ option(WITH_TESTS "Build unit tests (requires internet connection)" ON)
option(WITH_GUI "Build GUI items" ON)
option(WITH_SIMULATION_MODULES "Build simulation modules" ON)
option(WITH_PROTOBUF "Build protobuf support" ON)
option(WITH_BENCHMARK "Build the benchmark project" ON)
# Options for using a package manager (e.g., vcpkg) for certain dependencies.
option(USE_SYSTEM_FMTLIB "Use system fmtlib" OFF)
@@ -288,6 +289,7 @@ endif()
set(FILENAME_DEP_REPLACE "get_filename_component(SELF_DIR \"$\{CMAKE_CURRENT_LIST_FILE\}\" PATH)")
set(SELF_DIR "$\{SELF_DIR\}")
set(WPIUNITS_DEP_REPLACE_IMPL "find_dependency(wpiunits)")
set(WPIANNOTATIONS_DEP_REPLACE_IMPL "find_dependency(wpiannotations)")
set(WPIUTIL_DEP_REPLACE "find_dependency(wpiutil)")
add_subdirectory(wpiutil)
@@ -307,6 +309,10 @@ if(WITH_WPIMATH)
add_subdirectory(wpimath)
endif()
if(WITH_JAVA)
add_subdirectory(wpiannotations)
endif()
if(WITH_WPIUNITS AND NOT WITH_WPIMATH)
# In case of building wpiunits standalone
set(WPIUNITS_DEP_REPLACE ${WPIUNITS_DEP_REPLACE_IMPL})
@@ -345,7 +351,9 @@ endif()
if(WITH_WPILIB)
set(APRILTAG_DEP_REPLACE "find_dependency(apriltag)")
set(WPILIBC_DEP_REPLACE "find_dependency(wpilibc)")
set(WPILIBJ_DEP_REPLACE "find_dependency(wpilibj)")
if(WITH_JAVA)
set(WPILIBJ_DEP_REPLACE "find_dependency(wpilibj)")
endif()
set(WPILIBNEWCOMMANDS_DEP_REPLACE "find_dependency(wpilibNewCommands)")
add_subdirectory(apriltag)
add_subdirectory(wpilibj)
@@ -359,6 +367,10 @@ if(WITH_WPILIB)
add_subdirectory(developerRobot)
endif()
if(WITH_BENCHMARK)
add_subdirectory(benchmark)
endif()
if(WITH_SIMULATION_MODULES)
add_subdirectory(simulation)
endif()

View File

@@ -1,69 +1,46 @@
# Contributor Covenant Code of Conduct
# Contributor Community Code of Conduct
## Our Pledge
We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, religion, or sexual identity
and orientation.
As members, contributors, and leaders, we commit to fostering a community where everyone feels safe, respected, and valued. We are dedicated to ensuring that participation in this community is harassment-free, inclusive, and welcoming, regardless of age, body type, abilities (visible or invisible), ethnicity, gender identity or expression, sexual orientation, socioeconomic background, education, nationality, personal appearance, race, or religion.
We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.
We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community.
## Our Standards
Examples of behavior that contributes to a positive environment for our
community include:
Positive and respectful behavior is essential to creating a thriving community. This includes:
* Exhibiting Gracious Professionalism® at all times. Gracious Professionalism
* Practicing **Gracious Professionalism®** at all times. Gracious Professionalism
is a way of doing things that encourages high-quality work, emphasizes the
value of others, and respects individuals and the community.
* Demonstrating empathy and kindness toward other people
* Being respectful of differing opinions, viewpoints, and experiences
* Giving and gracefully accepting constructive feedback
* Accepting responsibility and apologizing to those affected by our mistakes,
and learning from the experience
* Focusing on what is best not just for us as individuals, but for the
overall community
* Showing empathy, kindness, and patience.
* Respecting diverse perspectives and experiences.
* Giving and receiving constructive feedback with openness and humility.
* Owning mistakes, apologizing when necessary, and learning from them.
* Prioritizing the well-being and success of the entire community over individual interests.
Examples of unacceptable behavior include:
Unacceptable behavior include:
* The use of sexualized language or imagery, and sexual attention or
advances of any kind
* Using sexualized language, imagery, or making inappropriate advances
* Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or email
address, without their explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
* Harassment in any form, whether public or private.
* Sharing private information (e.g., email or physical addresses) without explicit consent.
* Any behavior that is unprofessional, harmful, or exclusionary.
## Enforcement Responsibilities
Community leaders are responsible for clarifying and enforcing our standards of
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.
Community leaders have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, and will communicate reasons for moderation
decisions when appropriate.
Community leaders are responsible for maintaining these standards and will take appropriate action to address any behavior deemed harmful, threatening, or inappropriate. Actions may include removing content, issuing warnings, or, when necessary, banning individuals. Moderation decisions will be communicated transparently where appropriate.
## Scope
This Code of Conduct applies within all community spaces, and also applies when
an individual is officially representing the community in public spaces.
Examples of representing our community include using an official e-mail address,
posting via an official social media account, or acting as an appointed
representative at an online or offline event.
This Code of Conduct applies to all community spaces, events, and instances where individuals represent the community (e.g., official email accounts, social media posts, or in-person/virtual events).
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at
[conduct@wpilib.org](mailto:conduct@wpilib.org).
[wpilib@wpi.edu](mailto:wpilib@wpi.edu).
All complaints will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the
@@ -79,7 +56,7 @@ the consequences for any action they deem in violation of this Code of Conduct:
**Community Impact**: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community.
**Consequence**: A private, written warning from community leaders, providing
**Consequence**: A warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.
@@ -115,6 +92,9 @@ individual, or aggression toward or disparagement of classes of individuals.
**Consequence**: A permanent ban from any sort of public interaction within
the community.
## A Note on Kindness
Building a community isnt just about rules—its about connection. Every interaction is an opportunity to be understanding, compassionate, and supportive. Being a good human is the key to our ethos.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage],

View File

@@ -4,6 +4,7 @@ So you want to contribute your changes back to WPILib. Great! We have a few cont
- [General Contribution Rules](#general-contribution-rules)
- [What to Contribute](#what-to-contribute)
- [Contribution Process](#contribution-process)
- [Coding Guidelines](#coding-guidelines)
- [Submitting Changes](#submitting-changes)
- [Pull Request Format](#pull-request-format)
@@ -30,57 +31,33 @@ So you want to contribute your changes back to WPILib. Great! We have a few cont
- Features must be added to Java (WPILibJ), C++ (WPILibC), with rare exceptions.
- Most of Python (RobotPy) is created by wrapping WPILibC with pybind11 via robotpy-build. However, new features to the command framework should also be submitted to [robotpy-commands-v2](https://github.com/robotpy/robotpy-commands-v2) as the command framework is reimplemented in Python.
- During competition season, we will not merge any new feature additions. We want to ensure that the API is stable during the season to help minimize issues for teams.
- Ask about large changes before spending a bunch of time on them! You can create a new issue on our GitHub tracker for feature request/discussion and talk about it with us there.
- Ask about large changes before spending a bunch of time on them! See [Contribution Process](#contribution-process) for where to ask.
- Features that make it easier for teams with less experience to be more successful are more likely to be accepted.
- Features in WPILib should be broadly applicable to all teams. Anything that is team specific should not be submitted.
- As a rule, we are happy with the general structure of WPILib. We are not interested in major rewrites of all of WPILib. We are open to talking about ideas, but backwards compatibility is very important for WPILib, so be sure to keep this in mind when proposing major changes.
- Generally speaking, we do not accept code for specific sensors. We have to be able to test the sensor in hardware on the WPILib test bed. Additionally, hardware availability for teams is important. Therefore, as a general rule, the library only directly supports hardware that is in the Kit of Parts. If you are a company interested in getting a sensor into the Kit of Parts, please contact FIRST directly at frcparts@firstinspires.org.
## Contribution Process
Have an idea to make WPILib better? Here's some steps to go from idea to implementation:
1. (Optional) **Discuss it in the Discord.** The programming discussion channel in the [Unofficial FIRST Robotics Competition Discord Server](https://discord.com/invite/frc) is a popular choice for initial discussion about ideas because many WPILib developers are active there and the live messaging nature of the platform is well suited for initial discussion (particularly for smaller changes). Note that the unofficial Discord server is not a mandatory step in the development process and is not endorsed by FIRST®.
2. (Recommended) **Open a GitHub issue.** GitHub issues are another way to get initial feedback about an idea before working on an implementation. Compared to the unofficial Discord server, GitHub issues have much wider visibility and are better suited for serious discussions about major changes. Getting feedback about an idea (whether in the unofficial Discord server or in a GitHub issue) before working on the implementation is recommended to avoid working on a change that will be rejected, though some ideas are pretty safe.
3. (Rare) **Create a design document in GitHub.** Sometimes, a change is so large that a design document is necessary to fully flesh out the details (and get feedback on them) before starting on an implementation. This is done through a pull request that adds the design document (as a Markdown file) to the repository. This is extremely rare, and it is sometimes done concurrently with the implementation if the change doesn't need much debate but is large enough to require a design document.
4. (Mandatory) **Create a GitHub pull request.** This is how you implement the changes, and is the focus of most of the rest of this document.
## Coding Guidelines
WPILib uses modified Google style guides for both C++ and Java, which can be found in the [styleguide repository](https://github.com/wpilibsuite/styleguide). Autoformatters are available for many popular editors at https://github.com/google/styleguide. Running wpiformat is required for all contributions and is enforced by our continuous integration system.
While the library should be fully formatted according to the styles, additional elements of the style guide were not followed when the library was initially created. All new code should follow the guidelines. If you are looking for some easy ramp-up tasks, finding areas that don't follow the style guide and fixing them is very welcome.
### Math documentation
When writing math expressions in documentation, use https://www.unicodeit.net/ to convert LaTeX to a Unicode equivalent that's easier to read. Not all expressions will translate (e.g., superscripts of superscripts) so focus on making it readable by someone who isn't familiar with LaTeX. If content on multiple lines needs to be aligned in Doxygen/Javadoc comments (e.g., integration/summation limits, matrices packed with square brackets and superscripts for them), put them in @verbatim/@endverbatim blocks in Doxygen or `<pre>` tags in Javadoc so they render with monospace font.
The LaTeX to Unicode conversions can also be done locally via the unicodeit Python package. To install it, execute:
```bash
pip install --user unicodeit
```
Here's example usage:
```bash
$ python -m unicodeit.cli 'x_{k+1} = Ax_k + Bu_k'
xₖ₊₁ = Axₖ + Buₖ
```
On Linux, this process can be streamlined further by adding the following Bash function to your .bashrc (requires `wl-clipboard` on Wayland or `xclip` on X11):
```bash
# Converts LaTeX to Unicode, prints the result, and copies it to the clipboard
uc() {
if [ $WAYLAND_DISPLAY ]; then
python -m unicodeit.cli $@ | tee >(wl-copy -n)
else
python -m unicodeit.cli $@ | tee >(xclip -sel)
fi
}
```
Here's example usage:
```bash
$ uc 'x_{k+1} = Ax_k + Bu_k'
xₖ₊₁ = Axₖ + Buₖ
```
## Submitting Changes
### Pull Request Format
Changes should be submitted as a Pull Request against the main branch of WPILib. For most changes, commits will be squashed upon merge. For particularly large changes, multiple commits are ok, but assume one commit unless asked otherwise. We may ask you to break a PR into multiple standalone PRs or commits for rebase within one PR to separate unrelated changes. No change will be merged unless it is up to date with the current main branch. We do this to make sure that the git history isn't too cluttered.
During the build season, breaking changes or other changes intended for the next season can be created as a pull request against the development branch of WPILib. After the season is over, the changes in the development branch will be merged into main.
Particularly large and/or breaking changes should be targeted to the 2027 branch, which targets the [SystemCore Robot Controller](https://community.firstinspires.org/introducing-the-future-mobile-robot-controller). The intent is minimize changes for 2026, to allow development to focus on preparing for 2027.
### Merge Process

View File

@@ -13,7 +13,7 @@ This article contains instructions on building projects using a development buil
Development builds are the per-commit build hosted every time a commit is pushed to the [allwpilib](https://github.com/wpilibsuite/allwpilib/) repository. These builds are then hosted on [artifactory](https://frcmaven.wpi.edu/artifactory/webapp/#/home).
To build a project using a development build, find the build.gradle file and open it. Then, add the following code below the plugin section and replace YEAR with the year of the development version. It is also necessary to use a 2025 GradleRIO version, ie `2025.1.1-beta-1`
To build a project using a development build, find the build.gradle file and open it. Then, add the following code below the plugin section and replace YEAR with the year of the development version. It is also necessary to use a 2026 GradleRIO version, ie `2026.1.1`
```groovy
wpi.maven.useLocal = false
@@ -28,13 +28,13 @@ Java
```groovy
plugins {
id "java"
id "edu.wpi.first.GradleRIO" version "2025.1.1-beta-1"
id "edu.wpi.first.GradleRIO" version "2026.1.1"
}
wpi.maven.useLocal = false
wpi.maven.useDevelopment = true
wpi.versions.wpilibVersion = '2025.+'
wpi.versions.wpimathVersion = '2025.+'
wpi.versions.wpilibVersion = '2026.+'
wpi.versions.wpimathVersion = '2026.+'
```
C++
@@ -42,13 +42,13 @@ C++
plugins {
id "cpp"
id "google-test-test-suite"
id "edu.wpi.first.GradleRIO" version "2025.1.1-beta-1"
id "edu.wpi.first.GradleRIO" version "2026.1.1"
}
wpi.maven.useLocal = false
wpi.maven.useDevelopment = true
wpi.versions.wpilibVersion = '2025.+'
wpi.versions.wpimathVersion = '2025.+'
wpi.versions.wpilibVersion = '2026.+'
wpi.versions.wpimathVersion = '2026.+'
```
### Development Build Documentation
@@ -64,7 +64,7 @@ Java
```groovy
plugins {
id "java"
id "edu.wpi.first.GradleRIO" version "2025.1.1-beta-1"
id "edu.wpi.first.GradleRIO" version "2026.1.1"
}
wpi.maven.useLocal = false
@@ -78,7 +78,7 @@ C++
plugins {
id "cpp"
id "google-test-test-suite"
id "edu.wpi.first.GradleRIO" version "2025.1.1-beta-1"
id "edu.wpi.first.GradleRIO" version "2026.1.1"
}
wpi.maven.useLocal = false

View File

@@ -1,4 +1,4 @@
Copyright (c) 2009-2025 FIRST and other WPILib contributors
Copyright (c) 2009-2026 FIRST and other WPILib contributors
All rights reserved.
Redistribution and use in source and binary forms, with or without

View File

@@ -14,54 +14,27 @@ The development repository is where development releases of every commit to [mai
## Artifact classifiers
We provide two base types of artifacts.
The first types are Java artifacts. These are usually published as `jar` files. Usually, the actual jar file is published with no classifier. The sources are published with the `-sources` classifier, and the javadocs are published with the `-javadoc` classifier.
The second types are native artifacts. These are usually published as `zip` files (except for the `JNI` artifact types, which are `jar` files. See below for information on this). The `-sources` and `-headers` classifiers contain the sources and headers respectively for the library. Each artifact also contains a classifier for each platform we publish. This platform is in the format `{os}{arch}`. The platform artifact only contains the binaries for a specific platform. In addition, we provide a `-all` classifier. This classifier combines all of the platform artifacts into a single artifact. This is useful for tools that cannot determine what version to use during builds. However, we recommend using the platform specific classifier when possible. Note that the binary artifacts never contain the headers, you always need the `-headers` classifier to get those.
## Artifact Names
WPILib builds four different types of artifacts.
##### C++ Only Libraries
When we publish C++ only libraries, they are published with the base artifact name as their artifact name, with a `-cpp` extension. All dependencies for the library are linked as shared libraries to the binary.
Example:
```
edu.wpi.first.wpilibc:wpilibc-cpp:version:classifier@zip
```
#### Java Only Libraries
When we publish Java only libraries, they are published with the base artifact name as their artifact name, with a `-java` extension.
The first types are Java artifacts. These are usually published as `jar` files. Usually, the actual jar file is published with no classifier. The sources are published with the `-sources` classifier, and the javadocs are published with the `-javadoc` classifier. These artifacts are published with the base artifact name as their artifact ID, with a `-java` extension.
Example:
```
edu.wpi.first.wpilibj:wpilibj-java:version
```
#### C++/Java Libraries without JNI
For libraries that are both C++ and Java, but without a JNI component, the C++ component is published with the `basename-cpp` artifact name, and the Java component is published with the `basename-java` artifact name.
The second types are native artifacts. These are usually published as `zip` files. The `-sources` and `-headers` classifiers contain the sources and headers respectively for the library. Each artifact also contains a classifier for each platform we publish. This platform is in the format `{os}{arch}`. The full list of supported platforms can be found in [native-utils](https://github.com/wpilibsuite/native-utils/blob/main/src/main/java/edu/wpi/first/nativeutils/WPINativeUtilsExtension.java#L94). If the library is built statically, it will have `static` appended to the classifier. Additionally, if the library was built in debug mode, `debug` will be appended to the classifier. The platform artifact only contains the binaries for a specific platform. Note that the binary artifacts never contain the headers, you always need the `-headers` classifier to get those.
If the library is Java and C++ and has a JNI component, the native artifact will have a shared library containing JNI entrypoints alongside the C++ shared library. This JNI shared library will have a `jni` suffix in the file name.
Native artifacts are published with the base artifact name as their artifact ID, with a `-cpp` extension.
Example:
```
edu.wpi.first.wpiutil:wpiutil-cpp:version:classifier@zip (C++)
edu.wpi.first.wpiutil:wpiutil-java:version (Java)
```
#### C++/Java Libraries with JNI
For libraries that are both C++ and Java with a JNI component there are three different artifact names. For Java, the component is published as `basename-java`. For C++, the `basename-cpp` artifact contains the C++ artifacts with all dependencies linked as shared libraries to the binary. These binaries DO contain the JNI entry points. The `basename-jni` artifact contains identical C++ binaries to the `-cpp` artifact, however all of its dependencies are statically linked, and only the JNI and C entry points are exported.
The `-jni` artifact should only be used in cases where you want to create a self contained Java application where the native artifacts are embedded in the jar. Note in an extraction scenario, extending off of the library is never supported, which is why the C++ entry points are not exposed. The name of the library is randomly generated during extraction. For pretty much all cases, and if you ever want to extend from a native library, you should use the `-cpp` artifacts. GradleRIO uses the `-cpp` artifacts for all platforms, even desktop, for this reason.
Example:
```
edu.wpi.first.ntcore:ntcore-cpp:version:classifier@zip (C++)
edu.wpi.first.ntcore:ntcore-jni:version:classifier (JNI jar library)
edu.wpi.first.ntcore:ntcore-java:version (Java)
edu.wpi.first.wpimath:wpimath-cpp:version:classifier@zip
edu.wpi.first.wpimath:wpimath-cpp:version:windowsx86-64staticdebug@zip
```
## Provided Artifacts
This repository provides the following artifacts. Below each artifact is its dependencies. Note if ever using the `-jni` artifacts, no dependencies are needed for native binaries.
This repository provides the following artifacts. Below each artifact is its dependencies.
For C++, if building with static dependencies, the listed order should be the link order in your linker.

View File

@@ -50,7 +50,7 @@ Using Gradle makes building WPILib very straightforward. It only has a few depen
- C++ compiler
- On Linux, install GCC 11 or greater
- On Windows, install [Visual Studio Community 2022](https://visualstudio.microsoft.com/vs/community/) and select the C++ programming language during installation (Gradle can't use the build tools for Visual Studio)
- On macOS, install the Xcode command-line build tools via `xcode-select --install`. Xcode 13 or later is required.
- On macOS, install the Xcode command-line build tools via `xcode-select --install`. Xcode 14 or later is required.
- ARM compiler toolchain
- Run `./gradlew installRoboRioToolchain` after cloning this repository
- If the WPILib installer was used, this toolchain is already installed
@@ -59,8 +59,6 @@ Using Gradle makes building WPILib very straightforward. It only has a few depen
On macOS ARM, run `softwareupdate --install-rosetta`. This is necessary to be able to use the macOS x86 roboRIO toolchain on ARM.
On linux, run `sudo apt install gfortran`. This is necessary to be able to build WPIcal on linux platforms.
## Setup
Clone the WPILib repository and follow the instructions above for installing any required tooling. The build process uses versioning information from git. Downloading the source is not sufficient to run the build.
@@ -136,6 +134,9 @@ If you have installed the FRC Toolchain to a directory other than the default, o
Once a PR has been submitted, formatting can be run in CI by commenting `/format` on the PR. A new commit will be pushed with the formatting changes.
> [!NOTE]
> The `/format` action has been temporarily disabled. The individual formatting commands can be run locally as shown below. Alternately, the Lint and Format action for a PR will upload a patch file that can be downloaded and applied manually.
#### wpiformat
wpiformat can be executed anywhere in the repository via `py -3 -m wpiformat` on Windows or `python3 -m wpiformat` on other platforms.

View File

@@ -33,7 +33,6 @@ jQuery wpinet/src/main/native/resources/jquery-*
popper.js wpinet/src/main/native/resources/popper-*
units wpimath/src/main/native/include/units/
Eigen wpimath/src/main/native/thirdparty/eigen/include/
StackWalker wpiutil/src/main/native/windows/StackWalker.*
Team 254 Library wpimath/src/main/java/edu/wpi/first/math/spline/SplineParameterizer.java
wpimath/src/main/java/edu/wpi/first/math/trajectory/TrajectoryParameterizer.java
wpimath/src/main/native/include/frc/spline/SplineParameterizer.h
@@ -49,13 +48,13 @@ apriltag apriltag/src/main/native/thirdparty/apriltag
glfw thirdparty/imgui_suite/glfw
Dear ImGui thirdparty/imgui_suite/imgui
implot thirdparty/imgui_suite/implot
memory wpiutil/src/main/native/thirdparty/memory
nanopb wpiutil/src/main/native/thirdparty/nanopb
protobuf wpiutil/src/main/native/thirdparty/protobuf
mrcal wpical/src/main/native/thirdparty/mrcal
libdogleg wpical/src/main/native/thirdparty/libdogleg
Simd hal/src/main/native/athena/simd
Additionally, glfw, memory, and nanopb were all modified for use in WPILib.
Additionally, glfw and nanopb were modified for use in WPILib.
==============================================================================
Google Test License
@@ -1025,35 +1024,6 @@ Exhibit B - "Incompatible With Secondary Licenses" Notice
defined by the Mozilla Public License, v. 2.0.
===================
StackWalker License
===================
Copyright (c) 2005-2013, Jochen Kalmbach
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
Neither the name of Jochen Kalmbach nor the names of its contributors may be
used to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
================
Team 254 Library
================
@@ -1360,31 +1330,6 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
==============
memory License
==============
Copyright (C) 2015-2023 Jonathan Müller and foonathan/memory contributors
This software is provided 'as-is', without any express or
implied warranty. In no event will the authors be held
liable for any damages arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute
it freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented;
you must not claim that you wrote the original software.
If you use this software in a product, an acknowledgment
in the product documentation would be appreciated but
is not required.
2. Altered source versions must be plainly marked as such,
and must not be misrepresented as being the original software.
3. This notice may not be removed or altered from any
source distribution.
==============
nanopb License
==============
@@ -1702,3 +1647,29 @@ This program is free software: you can redistribute it and/or modify it under th
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
The full text of the license is available at http://www.gnu.org/licenses
============
Simd License
============
MIT License
Copyright (c) 2011-2017 Ihar Yermalayeu
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -15,12 +15,12 @@ rules_jvm_external_deps()
load("@rules_jvm_external//:defs.bzl", "maven_install")
maven_artifacts = [
"org.ejml:ejml-simple:0.43.1",
"com.fasterxml.jackson.core:jackson-annotations:2.15.2",
"com.fasterxml.jackson.core:jackson-core:2.15.2",
"com.fasterxml.jackson.core:jackson-databind:2.15.2",
"us.hebi.quickbuf:quickbuf-runtime:1.3.3",
"com.google.code.gson:gson:2.10.1",
"org.ejml:ejml-simple:0.44.0",
"com.fasterxml.jackson.core:jackson-annotations:2.19.2",
"com.fasterxml.jackson.core:jackson-core:2.19.2",
"com.fasterxml.jackson.core:jackson-databind:2.19.2",
"us.hebi.quickbuf:quickbuf-runtime:1.4",
"com.google.code.gson:gson:2.13.1",
]
maven_install(
@@ -35,8 +35,8 @@ maven_install(
# Download toolchains
http_archive(
name = "rules_bzlmodrio_toolchains",
sha256 = "fe267e2af53c1def1e962700a9aeda9e8fdfa9fb46b72167c615ec0e25447dd6",
url = "https://github.com/wpilibsuite/rules_bzlmodRio_toolchains/releases/download/2025-1/rules_bzlmodRio_toolchains-2025-1.tar.gz",
sha256 = "ff25b5f9445cbd43759be4c6582b987d1065cf817c593eedc7ada1a699298c84",
url = "https://github.com/wpilibsuite/rules_bzlmodRio_toolchains/releases/download/2025-1.bcr2/rules_bzlmodRio_toolchains-2025-1.bcr2.tar.gz",
)
load("@rules_bzlmodrio_toolchains//:maven_deps.bzl", "setup_legacy_setup_toolchains_dependencies")
@@ -50,8 +50,8 @@ load_toolchains()
#
http_archive(
name = "rules_bzlmodrio_jdk",
sha256 = "a00d5fa971fbcad8a17b1968cdc5350688397035e90b0cb94e040d375ecd97b4",
url = "https://github.com/wpilibsuite/rules_bzlmodRio_jdk/releases/download/17.0.8.1-1/rules_bzlmodRio_jdk-17.0.8.1-1.tar.gz",
sha256 = "81869fe9860e39b17e4a9bc1d33c1ca2faede7e31d9538ed0712406f753a2163",
url = "https://github.com/wpilibsuite/rules_bzlmodRio_jdk/releases/download/17.0.12-7/rules_bzlmodRio_jdk-17.0.12-7.tar.gz",
)
load("@rules_bzlmodrio_jdk//:maven_deps.bzl", "setup_legacy_setup_jdk_dependencies")
@@ -62,9 +62,15 @@ register_toolchains(
"@local_roborio//:macos",
"@local_roborio//:linux",
"@local_roborio//:windows",
"@local_raspi_32//:macos",
"@local_raspi_32//:linux",
"@local_raspi_32//:windows",
"@local_systemcore//:macos",
"@local_systemcore//:linux",
"@local_systemcore//:windows",
"@local_raspi_bullseye_32//:macos",
"@local_raspi_bullseye_32//:linux",
"@local_raspi_bullseye_32//:windows",
"@local_raspi_bookworm_32//:macos",
"@local_raspi_bookworm_32//:linux",
"@local_raspi_bookworm_32//:windows",
"@local_bullseye_32//:macos",
"@local_bullseye_32//:linux",
"@local_bullseye_32//:windows",
@@ -83,8 +89,8 @@ setup_legacy_setup_jdk_dependencies()
http_archive(
name = "bzlmodrio-ni",
sha256 = "197fceac88bf44fb8427d5e000b0083118d3346172dd2ad31eccf83a5e61b3ce",
url = "https://github.com/wpilibsuite/bzlmodRio-ni/releases/download/2025.0.0/bzlmodRio-ni-2025.0.0.tar.gz",
sha256 = "fff62c3cb3e83f9a0d0a01f1739477c9ca5e9a6fac05be1ad59dafcd385801f7",
url = "https://github.com/wpilibsuite/bzlmodRio-ni/releases/download/2025.2.0/bzlmodRio-ni-2025.2.0.tar.gz",
)
load("@bzlmodrio-ni//:maven_cpp_deps.bzl", "setup_legacy_bzlmodrio_ni_cpp_dependencies")
@@ -93,8 +99,8 @@ setup_legacy_bzlmodrio_ni_cpp_dependencies()
http_archive(
name = "bzlmodrio-opencv",
sha256 = "4f4a607956ca8555618736c3058dd96e09d02df19e95088c1e352d2319fd70c7",
url = "https://github.com/wpilibsuite/bzlmodRio-opencv/releases/download/2025.4.10.0-2/bzlmodRio-opencv-2025.4.10.0-2.tar.gz",
sha256 = "ba3f4910ce9cc0e08abff732aeb5835b1bcfd864ca5296edeadcf2935f7e81b9",
url = "https://github.com/wpilibsuite/bzlmodRio-opencv/releases/download/2025.4.10.0-3.bcr1/bzlmodRio-opencv-2025.4.10.0-3.bcr1.tar.gz",
)
load("@bzlmodrio-opencv//:maven_cpp_deps.bzl", "setup_legacy_bzlmodrio_opencv_cpp_dependencies")

View File

@@ -103,14 +103,8 @@ model {
return
}
it.cppCompiler.define 'WPILIB_EXPORTS'
if (it.component.name == "${nativeName}JNI") {
lib project: ':wpimath', library: 'wpimath', linkage: 'static'
lib project: ':wpiutil', library: 'wpiutil', linkage: 'static'
} else {
lib project: ':wpimath', library: 'wpimath', linkage: 'shared'
lib project: ':wpiutil', library: 'wpiutil', linkage: 'shared'
}
lib project: ':wpimath', library: 'wpimath', linkage: 'shared'
lib project: ':wpiutil', library: 'wpiutil', linkage: 'shared'
}
}
tasks {

View File

@@ -152,9 +152,10 @@ public class AprilTagFieldLayout {
var pose =
switch (origin) {
case kBlueAllianceWallRightSide -> Pose3d.kZero;
case kRedAllianceWallRightSide -> new Pose3d(
new Translation3d(m_fieldDimensions.fieldLength, m_fieldDimensions.fieldWidth, 0),
new Rotation3d(0, 0, Math.PI));
case kRedAllianceWallRightSide ->
new Pose3d(
new Translation3d(m_fieldDimensions.fieldLength, m_fieldDimensions.fieldWidth, 0),
new Rotation3d(0, 0, Math.PI));
};
setOrigin(pose);
}

View File

@@ -14,14 +14,20 @@ public enum AprilTagFields {
k2023ChargedUp("2023-chargedup.json"),
/** 2024 Crescendo. */
k2024Crescendo("2024-crescendo.json"),
/** 2025 Reefscape. */
k2025Reefscape("2025-reefscape.json");
/** 2025 Reefscape Welded (see TU 12). */
k2025ReefscapeWelded("2025-reefscape-welded.json"),
/** 2025 Reefscape AndyMark (see TU 12). */
k2025ReefscapeAndyMark("2025-reefscape-andymark.json"),
/** 2026 Rebuilt Welded. */
k2026RebuiltWelded("2026-rebuilt-welded.json"),
/** 2026 Rebuilt AndyMark. */
k2026RebuiltAndymark("2026-rebuilt-andymark.json");
/** Base resource directory. */
public static final String kBaseResourceDir = "/edu/wpi/first/apriltag/";
/** Alias to the current game. */
public static final AprilTagFields kDefaultField = k2025Reefscape;
public static final AprilTagFields kDefaultField = k2026RebuiltWelded;
/** Resource filename. */
public final String m_resourceFile;

View File

@@ -133,7 +133,10 @@ namespace frc {
std::string_view GetResource_2022_rapidreact_json();
std::string_view GetResource_2023_chargedup_json();
std::string_view GetResource_2024_crescendo_json();
std::string_view GetResource_2025_reefscape_json();
std::string_view GetResource_2025_reefscape_welded_json();
std::string_view GetResource_2025_reefscape_andymark_json();
std::string_view GetResource_2026_rebuilt_welded_json();
std::string_view GetResource_2026_rebuilt_andymark_json();
} // namespace frc
@@ -149,8 +152,17 @@ AprilTagFieldLayout AprilTagFieldLayout::LoadField(AprilTagField field) {
case AprilTagField::k2024Crescendo:
fieldString = GetResource_2024_crescendo_json();
break;
case AprilTagField::k2025Reefscape:
fieldString = GetResource_2025_reefscape_json();
case AprilTagField::k2025ReefscapeWelded:
fieldString = GetResource_2025_reefscape_welded_json();
break;
case AprilTagField::k2025ReefscapeAndyMark:
fieldString = GetResource_2025_reefscape_andymark_json();
break;
case AprilTagField::k2026RebuiltWelded:
fieldString = GetResource_2026_rebuilt_welded_json();
break;
case AprilTagField::k2026RebuiltAndyMark:
fieldString = GetResource_2026_rebuilt_andymark_json();
break;
case AprilTagField::kNumFields:
throw std::invalid_argument("Invalid Field");

View File

@@ -20,10 +20,16 @@ enum class AprilTagField {
k2023ChargedUp,
/// 2024 Crescendo.
k2024Crescendo,
/// 2025 Reefscape.
k2025Reefscape,
/// 2025 Reefscape AndyMark (see TU12).
k2025ReefscapeAndyMark,
/// 2025 Reefscape Welded (see TU12).
k2025ReefscapeWelded,
/// 2026 Rebuilt Andymark.
k2026RebuiltAndyMark,
/// 2026 Rebuilt Welded.
k2026RebuiltWelded,
/// Alias to the current game.
kDefaultField = k2025Reefscape,
kDefaultField = k2026RebuiltWelded,
// This is a placeholder for denoting the last supported field. This should
// always be the last entry in the enum and should not be used by users

View File

@@ -0,0 +1,23 @@
ID,X,Y,Z,Z-Rotation,X-Rotation
1,656.98,24.73,58.5,126,0
2,656.98,291.9,58.5,234,0
3,452.4,316.21,51.25,270,0
4,365.2,241.44,73.54,0,30
5,365.2,75.19,73.54,0,30
6,530.49,129.97,12.13,300,0
7,546.87,158.3,12.13,0,0
8,530.49,186.63,12.13,60,0
9,497.77,186.63,12.13,120,0
10,481.39,158.3,12.13,180,0
11,497.77,129.97,12.13,240,0
12,33.91,24.73,58.5,54,0
13,33.91,291.9,58.5,306,0
14,325.68,241.44,73.54,180,30
15,325.68,75.19,73.54,180,30
16,238.49,0.42,51.25,90,0
17,160.39,129.97,12.13,240,0
18,144,158.3,12.13,180,0
19,160.39,186.63,12.13,120,0
20,193.1,186.63,12.13,60,0
21,209.49,158.3,12.13,0,0
22,193.1,129.97,12.13,300,0
1 ID X Y Z Z-Rotation X-Rotation
2 1 656.98 24.73 58.5 126 0
3 2 656.98 291.9 58.5 234 0
4 3 452.4 316.21 51.25 270 0
5 4 365.2 241.44 73.54 0 30
6 5 365.2 75.19 73.54 0 30
7 6 530.49 129.97 12.13 300 0
8 7 546.87 158.3 12.13 0 0
9 8 530.49 186.63 12.13 60 0
10 9 497.77 186.63 12.13 120 0
11 10 481.39 158.3 12.13 180 0
12 11 497.77 129.97 12.13 240 0
13 12 33.91 24.73 58.5 54 0
14 13 33.91 291.9 58.5 306 0
15 14 325.68 241.44 73.54 180 30
16 15 325.68 75.19 73.54 180 30
17 16 238.49 0.42 51.25 90 0
18 17 160.39 129.97 12.13 240 0
19 18 144 158.3 12.13 180 0
20 19 160.39 186.63 12.13 120 0
21 20 193.1 186.63 12.13 60 0
22 21 209.49 158.3 12.13 0 0
23 22 193.1 129.97 12.13 300 0

View File

@@ -0,0 +1,404 @@
{
"tags": [
{
"ID": 1,
"pose": {
"translation": {
"x": 16.687292,
"y": 0.628142,
"z": 1.4859
},
"rotation": {
"quaternion": {
"W": 0.4539904997395468,
"X": 0.0,
"Y": 0.0,
"Z": 0.8910065241883678
}
}
}
},
{
"ID": 2,
"pose": {
"translation": {
"x": 16.687292,
"y": 7.414259999999999,
"z": 1.4859
},
"rotation": {
"quaternion": {
"W": -0.45399049973954675,
"X": -0.0,
"Y": 0.0,
"Z": 0.8910065241883679
}
}
}
},
{
"ID": 3,
"pose": {
"translation": {
"x": 11.49096,
"y": 8.031733999999998,
"z": 1.30175
},
"rotation": {
"quaternion": {
"W": -0.7071067811865475,
"X": -0.0,
"Y": 0.0,
"Z": 0.7071067811865476
}
}
}
},
{
"ID": 4,
"pose": {
"translation": {
"x": 9.276079999999999,
"y": 6.132575999999999,
"z": 1.8679160000000001
},
"rotation": {
"quaternion": {
"W": 0.9659258262890683,
"X": 0.0,
"Y": 0.25881904510252074,
"Z": 0.0
}
}
}
},
{
"ID": 5,
"pose": {
"translation": {
"x": 9.276079999999999,
"y": 1.9098259999999998,
"z": 1.8679160000000001
},
"rotation": {
"quaternion": {
"W": 0.9659258262890683,
"X": 0.0,
"Y": 0.25881904510252074,
"Z": 0.0
}
}
}
},
{
"ID": 6,
"pose": {
"translation": {
"x": 13.474446,
"y": 3.3012379999999997,
"z": 0.308102
},
"rotation": {
"quaternion": {
"W": -0.8660254037844387,
"X": -0.0,
"Y": 0.0,
"Z": 0.49999999999999994
}
}
}
},
{
"ID": 7,
"pose": {
"translation": {
"x": 13.890498,
"y": 4.0208200000000005,
"z": 0.308102
},
"rotation": {
"quaternion": {
"W": 1.0,
"X": 0.0,
"Y": 0.0,
"Z": 0.0
}
}
}
},
{
"ID": 8,
"pose": {
"translation": {
"x": 13.474446,
"y": 4.740402,
"z": 0.308102
},
"rotation": {
"quaternion": {
"W": 0.8660254037844387,
"X": 0.0,
"Y": 0.0,
"Z": 0.49999999999999994
}
}
}
},
{
"ID": 9,
"pose": {
"translation": {
"x": 12.643358,
"y": 4.740402,
"z": 0.308102
},
"rotation": {
"quaternion": {
"W": 0.5000000000000001,
"X": 0.0,
"Y": 0.0,
"Z": 0.8660254037844386
}
}
}
},
{
"ID": 10,
"pose": {
"translation": {
"x": 12.227305999999999,
"y": 4.0208200000000005,
"z": 0.308102
},
"rotation": {
"quaternion": {
"W": 6.123233995736766e-17,
"X": 0.0,
"Y": 0.0,
"Z": 1.0
}
}
}
},
{
"ID": 11,
"pose": {
"translation": {
"x": 12.643358,
"y": 3.3012379999999997,
"z": 0.308102
},
"rotation": {
"quaternion": {
"W": -0.4999999999999998,
"X": -0.0,
"Y": 0.0,
"Z": 0.8660254037844387
}
}
}
},
{
"ID": 12,
"pose": {
"translation": {
"x": 0.8613139999999999,
"y": 0.628142,
"z": 1.4859
},
"rotation": {
"quaternion": {
"W": 0.8910065241883679,
"X": 0.0,
"Y": 0.0,
"Z": 0.45399049973954675
}
}
}
},
{
"ID": 13,
"pose": {
"translation": {
"x": 0.8613139999999999,
"y": 7.414259999999999,
"z": 1.4859
},
"rotation": {
"quaternion": {
"W": -0.8910065241883678,
"X": -0.0,
"Y": 0.0,
"Z": 0.45399049973954686
}
}
}
},
{
"ID": 14,
"pose": {
"translation": {
"x": 8.272272,
"y": 6.132575999999999,
"z": 1.8679160000000001
},
"rotation": {
"quaternion": {
"W": 5.914589856893349e-17,
"X": -0.25881904510252074,
"Y": 1.5848095757158825e-17,
"Z": 0.9659258262890683
}
}
}
},
{
"ID": 15,
"pose": {
"translation": {
"x": 8.272272,
"y": 1.9098259999999998,
"z": 1.8679160000000001
},
"rotation": {
"quaternion": {
"W": 5.914589856893349e-17,
"X": -0.25881904510252074,
"Y": 1.5848095757158825e-17,
"Z": 0.9659258262890683
}
}
}
},
{
"ID": 16,
"pose": {
"translation": {
"x": 6.057646,
"y": 0.010667999999999999,
"z": 1.30175
},
"rotation": {
"quaternion": {
"W": 0.7071067811865476,
"X": 0.0,
"Y": 0.0,
"Z": 0.7071067811865476
}
}
}
},
{
"ID": 17,
"pose": {
"translation": {
"x": 4.073905999999999,
"y": 3.3012379999999997,
"z": 0.308102
},
"rotation": {
"quaternion": {
"W": -0.4999999999999998,
"X": -0.0,
"Y": 0.0,
"Z": 0.8660254037844387
}
}
}
},
{
"ID": 18,
"pose": {
"translation": {
"x": 3.6576,
"y": 4.0208200000000005,
"z": 0.308102
},
"rotation": {
"quaternion": {
"W": 6.123233995736766e-17,
"X": 0.0,
"Y": 0.0,
"Z": 1.0
}
}
}
},
{
"ID": 19,
"pose": {
"translation": {
"x": 4.073905999999999,
"y": 4.740402,
"z": 0.308102
},
"rotation": {
"quaternion": {
"W": 0.5000000000000001,
"X": 0.0,
"Y": 0.0,
"Z": 0.8660254037844386
}
}
}
},
{
"ID": 20,
"pose": {
"translation": {
"x": 4.904739999999999,
"y": 4.740402,
"z": 0.308102
},
"rotation": {
"quaternion": {
"W": 0.8660254037844387,
"X": 0.0,
"Y": 0.0,
"Z": 0.49999999999999994
}
}
}
},
{
"ID": 21,
"pose": {
"translation": {
"x": 5.321046,
"y": 4.0208200000000005,
"z": 0.308102
},
"rotation": {
"quaternion": {
"W": 1.0,
"X": 0.0,
"Y": 0.0,
"Z": 0.0
}
}
}
},
{
"ID": 22,
"pose": {
"translation": {
"x": 4.904739999999999,
"y": 3.3012379999999997,
"z": 0.308102
},
"rotation": {
"quaternion": {
"W": -0.8660254037844387,
"X": -0.0,
"Y": 0.0,
"Z": 0.49999999999999994
}
}
}
}
],
"field": {
"length": 17.548,
"width": 8.042
}
}

View File

@@ -0,0 +1,33 @@
ID,X,Y,Z,Z-Rotation,X-Rotation
1,467.085,291.791,35,180,0
2,468.559,182.077,44.25,90,0
3,444.797,172.321,44.25,180,0
4,444.797,158.321,44.25,180,0
5,468.559,134.565,44.25,270,0
6,467.085,24.851,35,180,0
7,470.034,24.851,35,0,0
8,482.559,134.565,44.25,270,0
9,492.329,144.321,44.25,0,0
10,492.329,158.321,44.25,0,0
11,482.559,182.077,44.25,90,0
12,470.034,291.791,35,0,0
13,649.58,291.02,21.75,180,0
14,649.58,274.02,21.75,180,0
15,649.566,169.783,21.75,180,0
16,649.566,152.783,21.75,180,0
17,183.034,24.851,35,0,0
18,181.559,134.565,44.25,270,0
19,205.321,144.321,44.25,0,0
20,205.321,158.321,44.25,0,0
21,181.559,182.077,44.25,90,0
22,183.034,291.791,35,0,0
23,180.085,291.791,35,180,0
24,167.559,182.077,44.25,90,0
25,157.79,172.321,44.25,180,0
26,157.79,158.321,44.25,180,0
27,167.559,134.565,44.25,270,0
28,180.085,24.851,35,180,0
29,0.539,25.621,21.75,0,0
30,0.539,42.621,21.75,0,0
31,0.553,146.858,21.75,0,0
32,0.553,163.858,21.75,0,0
1 ID X Y Z Z-Rotation X-Rotation
2 1 467.085 291.791 35 180 0
3 2 468.559 182.077 44.25 90 0
4 3 444.797 172.321 44.25 180 0
5 4 444.797 158.321 44.25 180 0
6 5 468.559 134.565 44.25 270 0
7 6 467.085 24.851 35 180 0
8 7 470.034 24.851 35 0 0
9 8 482.559 134.565 44.25 270 0
10 9 492.329 144.321 44.25 0 0
11 10 492.329 158.321 44.25 0 0
12 11 482.559 182.077 44.25 90 0
13 12 470.034 291.791 35 0 0
14 13 649.58 291.02 21.75 180 0
15 14 649.58 274.02 21.75 180 0
16 15 649.566 169.783 21.75 180 0
17 16 649.566 152.783 21.75 180 0
18 17 183.034 24.851 35 0 0
19 18 181.559 134.565 44.25 270 0
20 19 205.321 144.321 44.25 0 0
21 20 205.321 158.321 44.25 0 0
22 21 181.559 182.077 44.25 90 0
23 22 183.034 291.791 35 0 0
24 23 180.085 291.791 35 180 0
25 24 167.559 182.077 44.25 90 0
26 25 157.79 172.321 44.25 180 0
27 26 157.79 158.321 44.25 180 0
28 27 167.559 134.565 44.25 270 0
29 28 180.085 24.851 35 180 0
30 29 0.539 25.621 21.75 0 0
31 30 0.539 42.621 21.75 0 0
32 31 0.553 146.858 21.75 0 0
33 32 0.553 163.858 21.75 0 0

View File

@@ -0,0 +1,584 @@
{
"tags": [
{
"ID": 1,
"pose": {
"translation": {
"x": 11.863959,
"y": 7.411491399999999,
"z": 0.889
},
"rotation": {
"quaternion": {
"W": 6.123233995736766e-17,
"X": 0.0,
"Y": 0.0,
"Z": 1.0
}
}
}
},
{
"ID": 2,
"pose": {
"translation": {
"x": 11.9013986,
"y": 4.6247558,
"z": 1.12395
},
"rotation": {
"quaternion": {
"W": 0.7071067811865476,
"X": 0.0,
"Y": 0.0,
"Z": 0.7071067811865476
}
}
}
},
{
"ID": 3,
"pose": {
"translation": {
"x": 11.2978438,
"y": 4.3769534,
"z": 1.12395
},
"rotation": {
"quaternion": {
"W": 6.123233995736766e-17,
"X": 0.0,
"Y": 0.0,
"Z": 1.0
}
}
}
},
{
"ID": 4,
"pose": {
"translation": {
"x": 11.2978438,
"y": 4.0213534,
"z": 1.12395
},
"rotation": {
"quaternion": {
"W": 6.123233995736766e-17,
"X": 0.0,
"Y": 0.0,
"Z": 1.0
}
}
}
},
{
"ID": 5,
"pose": {
"translation": {
"x": 11.9013986,
"y": 3.417951,
"z": 1.12395
},
"rotation": {
"quaternion": {
"W": -0.7071067811865475,
"X": -0.0,
"Y": 0.0,
"Z": 0.7071067811865476
}
}
}
},
{
"ID": 6,
"pose": {
"translation": {
"x": 11.863959,
"y": 0.6312154,
"z": 0.889
},
"rotation": {
"quaternion": {
"W": 6.123233995736766e-17,
"X": 0.0,
"Y": 0.0,
"Z": 1.0
}
}
}
},
{
"ID": 7,
"pose": {
"translation": {
"x": 11.9388636,
"y": 0.6312154,
"z": 0.889
},
"rotation": {
"quaternion": {
"W": 1.0,
"X": 0.0,
"Y": 0.0,
"Z": 0.0
}
}
}
},
{
"ID": 8,
"pose": {
"translation": {
"x": 12.2569986,
"y": 3.417951,
"z": 1.12395
},
"rotation": {
"quaternion": {
"W": -0.7071067811865475,
"X": -0.0,
"Y": 0.0,
"Z": 0.7071067811865476
}
}
}
},
{
"ID": 9,
"pose": {
"translation": {
"x": 12.5051566,
"y": 3.6657534,
"z": 1.12395
},
"rotation": {
"quaternion": {
"W": 1.0,
"X": 0.0,
"Y": 0.0,
"Z": 0.0
}
}
}
},
{
"ID": 10,
"pose": {
"translation": {
"x": 12.5051566,
"y": 4.0213534,
"z": 1.12395
},
"rotation": {
"quaternion": {
"W": 1.0,
"X": 0.0,
"Y": 0.0,
"Z": 0.0
}
}
}
},
{
"ID": 11,
"pose": {
"translation": {
"x": 12.2569986,
"y": 4.6247558,
"z": 1.12395
},
"rotation": {
"quaternion": {
"W": 0.7071067811865476,
"X": 0.0,
"Y": 0.0,
"Z": 0.7071067811865476
}
}
}
},
{
"ID": 12,
"pose": {
"translation": {
"x": 11.9388636,
"y": 7.411491399999999,
"z": 0.889
},
"rotation": {
"quaternion": {
"W": 1.0,
"X": 0.0,
"Y": 0.0,
"Z": 0.0
}
}
}
},
{
"ID": 13,
"pose": {
"translation": {
"x": 16.499332,
"y": 7.391907999999999,
"z": 0.55245
},
"rotation": {
"quaternion": {
"W": 6.123233995736766e-17,
"X": 0.0,
"Y": 0.0,
"Z": 1.0
}
}
}
},
{
"ID": 14,
"pose": {
"translation": {
"x": 16.499332,
"y": 6.960107999999999,
"z": 0.55245
},
"rotation": {
"quaternion": {
"W": 6.123233995736766e-17,
"X": 0.0,
"Y": 0.0,
"Z": 1.0
}
}
}
},
{
"ID": 15,
"pose": {
"translation": {
"x": 16.4989764,
"y": 4.3124882,
"z": 0.55245
},
"rotation": {
"quaternion": {
"W": 6.123233995736766e-17,
"X": 0.0,
"Y": 0.0,
"Z": 1.0
}
}
}
},
{
"ID": 16,
"pose": {
"translation": {
"x": 16.4989764,
"y": 3.8806881999999994,
"z": 0.55245
},
"rotation": {
"quaternion": {
"W": 6.123233995736766e-17,
"X": 0.0,
"Y": 0.0,
"Z": 1.0
}
}
}
},
{
"ID": 17,
"pose": {
"translation": {
"x": 4.6490636,
"y": 0.6312154,
"z": 0.889
},
"rotation": {
"quaternion": {
"W": 1.0,
"X": 0.0,
"Y": 0.0,
"Z": 0.0
}
}
}
},
{
"ID": 18,
"pose": {
"translation": {
"x": 4.6115986,
"y": 3.417951,
"z": 1.12395
},
"rotation": {
"quaternion": {
"W": -0.7071067811865475,
"X": -0.0,
"Y": 0.0,
"Z": 0.7071067811865476
}
}
}
},
{
"ID": 19,
"pose": {
"translation": {
"x": 5.2151534,
"y": 3.6657534,
"z": 1.12395
},
"rotation": {
"quaternion": {
"W": 1.0,
"X": 0.0,
"Y": 0.0,
"Z": 0.0
}
}
}
},
{
"ID": 20,
"pose": {
"translation": {
"x": 5.2151534,
"y": 4.0213534,
"z": 1.12395
},
"rotation": {
"quaternion": {
"W": 1.0,
"X": 0.0,
"Y": 0.0,
"Z": 0.0
}
}
}
},
{
"ID": 21,
"pose": {
"translation": {
"x": 4.6115986,
"y": 4.6247558,
"z": 1.12395
},
"rotation": {
"quaternion": {
"W": 0.7071067811865476,
"X": 0.0,
"Y": 0.0,
"Z": 0.7071067811865476
}
}
}
},
{
"ID": 22,
"pose": {
"translation": {
"x": 4.6490636,
"y": 7.411491399999999,
"z": 0.889
},
"rotation": {
"quaternion": {
"W": 1.0,
"X": 0.0,
"Y": 0.0,
"Z": 0.0
}
}
}
},
{
"ID": 23,
"pose": {
"translation": {
"x": 4.574159,
"y": 7.411491399999999,
"z": 0.889
},
"rotation": {
"quaternion": {
"W": 6.123233995736766e-17,
"X": 0.0,
"Y": 0.0,
"Z": 1.0
}
}
}
},
{
"ID": 24,
"pose": {
"translation": {
"x": 4.2559986,
"y": 4.6247558,
"z": 1.12395
},
"rotation": {
"quaternion": {
"W": 0.7071067811865476,
"X": 0.0,
"Y": 0.0,
"Z": 0.7071067811865476
}
}
}
},
{
"ID": 25,
"pose": {
"translation": {
"x": 4.007866,
"y": 4.3769534,
"z": 1.12395
},
"rotation": {
"quaternion": {
"W": 6.123233995736766e-17,
"X": 0.0,
"Y": 0.0,
"Z": 1.0
}
}
}
},
{
"ID": 26,
"pose": {
"translation": {
"x": 4.007866,
"y": 4.0213534,
"z": 1.12395
},
"rotation": {
"quaternion": {
"W": 6.123233995736766e-17,
"X": 0.0,
"Y": 0.0,
"Z": 1.0
}
}
}
},
{
"ID": 27,
"pose": {
"translation": {
"x": 4.2559986,
"y": 3.417951,
"z": 1.12395
},
"rotation": {
"quaternion": {
"W": -0.7071067811865475,
"X": -0.0,
"Y": 0.0,
"Z": 0.7071067811865476
}
}
}
},
{
"ID": 28,
"pose": {
"translation": {
"x": 4.574159,
"y": 0.6312154,
"z": 0.889
},
"rotation": {
"quaternion": {
"W": 6.123233995736766e-17,
"X": 0.0,
"Y": 0.0,
"Z": 1.0
}
}
}
},
{
"ID": 29,
"pose": {
"translation": {
"x": 0.0136906,
"y": 0.6507734,
"z": 0.55245
},
"rotation": {
"quaternion": {
"W": 1.0,
"X": 0.0,
"Y": 0.0,
"Z": 0.0
}
}
}
},
{
"ID": 30,
"pose": {
"translation": {
"x": 0.0136906,
"y": 1.0825734,
"z": 0.55245
},
"rotation": {
"quaternion": {
"W": 1.0,
"X": 0.0,
"Y": 0.0,
"Z": 0.0
}
}
}
},
{
"ID": 31,
"pose": {
"translation": {
"x": 0.0140462,
"y": 3.7301932,
"z": 0.55245
},
"rotation": {
"quaternion": {
"W": 1.0,
"X": 0.0,
"Y": 0.0,
"Z": 0.0
}
}
}
},
{
"ID": 32,
"pose": {
"translation": {
"x": 0.0140462,
"y": 4.1619931999999995,
"z": 0.55245
},
"rotation": {
"quaternion": {
"W": 1.0,
"X": 0.0,
"Y": 0.0,
"Z": 0.0
}
}
}
}
],
"field": {
"length": 16.518,
"width": 8.043
}
}

View File

@@ -0,0 +1,33 @@
ID,X,Y,Z,Z-Rotation,X-Rotation
1,467.637,292.314,35,180,0
2,469.111,182.6,44.25,90,0
3,445.349,172.844,44.25,180,0
4,445.349,158.844,44.25,180,0
5,469.111,135.088,44.25,270,0
6,467.637,25.374,35,180,0
7,470.586,25.374,35,0,0
8,483.111,135.088,44.25,270,0
9,492.881,144.844,44.25,0,0
10,492.881,158.844,44.25,0,0
11,483.111,182.6,44.25,90,0
12,470.586,292.314,35,0,0
13,650.918,291.469,21.75,180,0
14,650.918,274.469,21.75,180,0
15,650.904,170.219,21.75,180,0
16,650.904,153.219,21.75,180,0
17,183.586,25.374,35,0,0
18,182.111,135.088,44.25,270,0
19,205.873,144.844,44.25,0,0
20,205.873,158.844,44.25,0,0
21,182.111,182.6,44.25,90,0
22,183.586,292.314,35,0,0
23,180.637,292.314,35,180,0
24,168.111,182.6,44.25,90,0
25,158.341,172.844,44.25,180,0
26,158.341,158.844,44.25,180,0
27,168.111,135.088,44.25,270,0
28,180.637,25.374,35,180,0
29,0.305,26.219,21.75,0,0
30,0.305,43.219,21.75,0,0
31,0.318,147.469,21.75,0,0
32,0.318,164.469,21.75,0,0
1 ID X Y Z Z-Rotation X-Rotation
2 1 467.637 292.314 35 180 0
3 2 469.111 182.6 44.25 90 0
4 3 445.349 172.844 44.25 180 0
5 4 445.349 158.844 44.25 180 0
6 5 469.111 135.088 44.25 270 0
7 6 467.637 25.374 35 180 0
8 7 470.586 25.374 35 0 0
9 8 483.111 135.088 44.25 270 0
10 9 492.881 144.844 44.25 0 0
11 10 492.881 158.844 44.25 0 0
12 11 483.111 182.6 44.25 90 0
13 12 470.586 292.314 35 0 0
14 13 650.918 291.469 21.75 180 0
15 14 650.918 274.469 21.75 180 0
16 15 650.904 170.219 21.75 180 0
17 16 650.904 153.219 21.75 180 0
18 17 183.586 25.374 35 0 0
19 18 182.111 135.088 44.25 270 0
20 19 205.873 144.844 44.25 0 0
21 20 205.873 158.844 44.25 0 0
22 21 182.111 182.6 44.25 90 0
23 22 183.586 292.314 35 0 0
24 23 180.637 292.314 35 180 0
25 24 168.111 182.6 44.25 90 0
26 25 158.341 172.844 44.25 180 0
27 26 158.341 158.844 44.25 180 0
28 27 168.111 135.088 44.25 270 0
29 28 180.637 25.374 35 180 0
30 29 0.305 26.219 21.75 0 0
31 30 0.305 43.219 21.75 0 0
32 31 0.318 147.469 21.75 0 0
33 32 0.318 164.469 21.75 0 0

View File

@@ -0,0 +1,584 @@
{
"tags": [
{
"ID": 1,
"pose": {
"translation": {
"x": 11.8779798,
"y": 7.4247756,
"z": 0.889
},
"rotation": {
"quaternion": {
"W": 6.123233995736766e-17,
"X": 0.0,
"Y": 0.0,
"Z": 1.0
}
}
}
},
{
"ID": 2,
"pose": {
"translation": {
"x": 11.9154194,
"y": 4.638039999999999,
"z": 1.12395
},
"rotation": {
"quaternion": {
"W": 0.7071067811865476,
"X": 0.0,
"Y": 0.0,
"Z": 0.7071067811865476
}
}
}
},
{
"ID": 3,
"pose": {
"translation": {
"x": 11.3118646,
"y": 4.3902376,
"z": 1.12395
},
"rotation": {
"quaternion": {
"W": 6.123233995736766e-17,
"X": 0.0,
"Y": 0.0,
"Z": 1.0
}
}
}
},
{
"ID": 4,
"pose": {
"translation": {
"x": 11.3118646,
"y": 4.0346376,
"z": 1.12395
},
"rotation": {
"quaternion": {
"W": 6.123233995736766e-17,
"X": 0.0,
"Y": 0.0,
"Z": 1.0
}
}
}
},
{
"ID": 5,
"pose": {
"translation": {
"x": 11.9154194,
"y": 3.4312351999999997,
"z": 1.12395
},
"rotation": {
"quaternion": {
"W": -0.7071067811865475,
"X": -0.0,
"Y": 0.0,
"Z": 0.7071067811865476
}
}
}
},
{
"ID": 6,
"pose": {
"translation": {
"x": 11.8779798,
"y": 0.6444996,
"z": 0.889
},
"rotation": {
"quaternion": {
"W": 6.123233995736766e-17,
"X": 0.0,
"Y": 0.0,
"Z": 1.0
}
}
}
},
{
"ID": 7,
"pose": {
"translation": {
"x": 11.9528844,
"y": 0.6444996,
"z": 0.889
},
"rotation": {
"quaternion": {
"W": 1.0,
"X": 0.0,
"Y": 0.0,
"Z": 0.0
}
}
}
},
{
"ID": 8,
"pose": {
"translation": {
"x": 12.2710194,
"y": 3.4312351999999997,
"z": 1.12395
},
"rotation": {
"quaternion": {
"W": -0.7071067811865475,
"X": -0.0,
"Y": 0.0,
"Z": 0.7071067811865476
}
}
}
},
{
"ID": 9,
"pose": {
"translation": {
"x": 12.519177399999998,
"y": 3.6790375999999996,
"z": 1.12395
},
"rotation": {
"quaternion": {
"W": 1.0,
"X": 0.0,
"Y": 0.0,
"Z": 0.0
}
}
}
},
{
"ID": 10,
"pose": {
"translation": {
"x": 12.519177399999998,
"y": 4.0346376,
"z": 1.12395
},
"rotation": {
"quaternion": {
"W": 1.0,
"X": 0.0,
"Y": 0.0,
"Z": 0.0
}
}
}
},
{
"ID": 11,
"pose": {
"translation": {
"x": 12.2710194,
"y": 4.638039999999999,
"z": 1.12395
},
"rotation": {
"quaternion": {
"W": 0.7071067811865476,
"X": 0.0,
"Y": 0.0,
"Z": 0.7071067811865476
}
}
}
},
{
"ID": 12,
"pose": {
"translation": {
"x": 11.9528844,
"y": 7.4247756,
"z": 0.889
},
"rotation": {
"quaternion": {
"W": 1.0,
"X": 0.0,
"Y": 0.0,
"Z": 0.0
}
}
}
},
{
"ID": 13,
"pose": {
"translation": {
"x": 16.5333172,
"y": 7.4033126,
"z": 0.55245
},
"rotation": {
"quaternion": {
"W": 6.123233995736766e-17,
"X": 0.0,
"Y": 0.0,
"Z": 1.0
}
}
}
},
{
"ID": 14,
"pose": {
"translation": {
"x": 16.5333172,
"y": 6.9715126,
"z": 0.55245
},
"rotation": {
"quaternion": {
"W": 6.123233995736766e-17,
"X": 0.0,
"Y": 0.0,
"Z": 1.0
}
}
}
},
{
"ID": 15,
"pose": {
"translation": {
"x": 16.5329616,
"y": 4.3235626,
"z": 0.55245
},
"rotation": {
"quaternion": {
"W": 6.123233995736766e-17,
"X": 0.0,
"Y": 0.0,
"Z": 1.0
}
}
}
},
{
"ID": 16,
"pose": {
"translation": {
"x": 16.5329616,
"y": 3.8917626,
"z": 0.55245
},
"rotation": {
"quaternion": {
"W": 6.123233995736766e-17,
"X": 0.0,
"Y": 0.0,
"Z": 1.0
}
}
}
},
{
"ID": 17,
"pose": {
"translation": {
"x": 4.6630844,
"y": 0.6444996,
"z": 0.889
},
"rotation": {
"quaternion": {
"W": 1.0,
"X": 0.0,
"Y": 0.0,
"Z": 0.0
}
}
}
},
{
"ID": 18,
"pose": {
"translation": {
"x": 4.6256194,
"y": 3.4312351999999997,
"z": 1.12395
},
"rotation": {
"quaternion": {
"W": -0.7071067811865475,
"X": -0.0,
"Y": 0.0,
"Z": 0.7071067811865476
}
}
}
},
{
"ID": 19,
"pose": {
"translation": {
"x": 5.229174199999999,
"y": 3.6790375999999996,
"z": 1.12395
},
"rotation": {
"quaternion": {
"W": 1.0,
"X": 0.0,
"Y": 0.0,
"Z": 0.0
}
}
}
},
{
"ID": 20,
"pose": {
"translation": {
"x": 5.229174199999999,
"y": 4.0346376,
"z": 1.12395
},
"rotation": {
"quaternion": {
"W": 1.0,
"X": 0.0,
"Y": 0.0,
"Z": 0.0
}
}
}
},
{
"ID": 21,
"pose": {
"translation": {
"x": 4.6256194,
"y": 4.638039999999999,
"z": 1.12395
},
"rotation": {
"quaternion": {
"W": 0.7071067811865476,
"X": 0.0,
"Y": 0.0,
"Z": 0.7071067811865476
}
}
}
},
{
"ID": 22,
"pose": {
"translation": {
"x": 4.6630844,
"y": 7.4247756,
"z": 0.889
},
"rotation": {
"quaternion": {
"W": 1.0,
"X": 0.0,
"Y": 0.0,
"Z": 0.0
}
}
}
},
{
"ID": 23,
"pose": {
"translation": {
"x": 4.5881798,
"y": 7.4247756,
"z": 0.889
},
"rotation": {
"quaternion": {
"W": 6.123233995736766e-17,
"X": 0.0,
"Y": 0.0,
"Z": 1.0
}
}
}
},
{
"ID": 24,
"pose": {
"translation": {
"x": 4.2700194,
"y": 4.638039999999999,
"z": 1.12395
},
"rotation": {
"quaternion": {
"W": 0.7071067811865476,
"X": 0.0,
"Y": 0.0,
"Z": 0.7071067811865476
}
}
}
},
{
"ID": 25,
"pose": {
"translation": {
"x": 4.0218614,
"y": 4.3902376,
"z": 1.12395
},
"rotation": {
"quaternion": {
"W": 6.123233995736766e-17,
"X": 0.0,
"Y": 0.0,
"Z": 1.0
}
}
}
},
{
"ID": 26,
"pose": {
"translation": {
"x": 4.0218614,
"y": 4.0346376,
"z": 1.12395
},
"rotation": {
"quaternion": {
"W": 6.123233995736766e-17,
"X": 0.0,
"Y": 0.0,
"Z": 1.0
}
}
}
},
{
"ID": 27,
"pose": {
"translation": {
"x": 4.2700194,
"y": 3.4312351999999997,
"z": 1.12395
},
"rotation": {
"quaternion": {
"W": -0.7071067811865475,
"X": -0.0,
"Y": 0.0,
"Z": 0.7071067811865476
}
}
}
},
{
"ID": 28,
"pose": {
"translation": {
"x": 4.5881798,
"y": 0.6444996,
"z": 0.889
},
"rotation": {
"quaternion": {
"W": 6.123233995736766e-17,
"X": 0.0,
"Y": 0.0,
"Z": 1.0
}
}
}
},
{
"ID": 29,
"pose": {
"translation": {
"x": 0.0077469999999999995,
"y": 0.6659626,
"z": 0.55245
},
"rotation": {
"quaternion": {
"W": 1.0,
"X": 0.0,
"Y": 0.0,
"Z": 0.0
}
}
}
},
{
"ID": 30,
"pose": {
"translation": {
"x": 0.0077469999999999995,
"y": 1.0977626,
"z": 0.55245
},
"rotation": {
"quaternion": {
"W": 1.0,
"X": 0.0,
"Y": 0.0,
"Z": 0.0
}
}
}
},
{
"ID": 31,
"pose": {
"translation": {
"x": 0.0080772,
"y": 3.7457125999999996,
"z": 0.55245
},
"rotation": {
"quaternion": {
"W": 1.0,
"X": 0.0,
"Y": 0.0,
"Z": 0.0
}
}
}
},
{
"ID": 32,
"pose": {
"translation": {
"x": 0.0080772,
"y": 4.1775126,
"z": 0.55245
},
"rotation": {
"quaternion": {
"W": 1.0,
"X": 0.0,
"Y": 0.0,
"Z": 0.0
}
}
}
}
],
"field": {
"length": 16.541,
"width": 8.069
}
}

View File

@@ -24,7 +24,6 @@ import org.opencv.core.Mat;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;
@SuppressWarnings("PMD.MutableStaticState")
class AprilTagDetectorTest {
@SuppressWarnings("MemberName")
AprilTagDetector detector;

44
benchmark/CMakeLists.txt Normal file
View File

@@ -0,0 +1,44 @@
project(benchmark)
include(CompileWarnings)
file(GLOB benchmarkCpp_src src/main/native/cpp/*.cpp src/main/native/thirdparty/benchmark/src/*.cpp)
add_executable(benchmarkCpp ${benchmarkCpp_src})
target_compile_features(benchmarkCpp PUBLIC cxx_std_20)
wpilib_target_warnings(benchmarkCpp)
target_link_libraries(
benchmarkCpp
PUBLIC
$<TARGET_NAME_IF_EXISTS:apriltag>
$<TARGET_NAME_IF_EXISTS:wpilibc>
$<TARGET_NAME_IF_EXISTS:wpilibNewCommands>
$<TARGET_NAME_IF_EXISTS:wpimath>
$<TARGET_NAME_IF_EXISTS:wpiutil>
)
# benchmark library setup
target_compile_definitions(benchmarkCpp PRIVATE benchmark_EXPORTS)
if(${CMAKE_SYSTEM_NAME} MATCHES "Windows")
target_link_libraries(benchmarkCpp PRIVATE shlwapi)
endif()
if(NOT BUILD_SHARED_LIBS)
target_compile_definitions(benchmarkCpp PUBLIC -DBENCHMARK_STATIC_DEFINE)
endif()
install(
DIRECTORY src/main/native/thirdparty/benchmark/include/
DESTINATION "${include_dest}/benchmark"
)
target_include_directories(
benchmarkCpp
SYSTEM
PRIVATE
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src/main/native/thirdparty/benchmark/include>
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src/main/native/thirdparty/benchmark/src>
)

49
benchmark/README.md Normal file
View File

@@ -0,0 +1,49 @@
# Benchmark
This is a benchmark project built directly against this repo's sources.
## Desktop benchmarking
This command runs the Java benchmarks on desktop.
```bash
./gradlew benchmark:run
```
This command runs the C++ benchmarks on desktop.
```
./gradlew benchmark:runCpp
```
## Deploy to a roboRIO
This project can only deploy over USB. If an alternate IP address is preferred, the `address` block in benchmark/build.gradle can be changed to point to another address.
This command deploys the C++ project using shared dependencies. Prefer this one for most C++ development.
```bash
./gradlew benchmark:deployShared
```
This command deploys the C++ project with all dependencies statically linked.
```bash
./gradlew benchmark:deployStatic
```
This command deploys the Java project and all required dependencies. It also installs the JRE if it's not currently installed.
```bash
./gradlew benchmark:deployJava
```
Those commands won't start the robot executable, so you have to manually ssh in and start it. The following command will do that.
```bash
ssh lvuser@172.22.11.2 frcRunRobot.sh
```
Console log prints will appear in the terminal.
Deploying any of these to the roboRIO will disable the current startup project until it is redeployed.
## Faster builds
If your benchmarks only need some projects, you can comment out or delete unnecessary subprojects from the dependencies, benchmarkCpp, and benchmarkCppStatic blocks in benchmark/build.gradle (Java or C++) and from `target_link_libraries()` in benchmark/CMakeLists.txt (C++ only).

343
benchmark/build.gradle Normal file
View File

@@ -0,0 +1,343 @@
import edu.wpi.first.deployutils.deploy.target.RemoteTarget
import edu.wpi.first.deployutils.deploy.target.location.SshDeployLocation
import edu.wpi.first.deployutils.deploy.artifact.*
import org.gradle.internal.os.OperatingSystem
plugins {
id 'java'
id 'application'
id 'cpp'
id 'visual-studio'
}
apply plugin: 'edu.wpi.first.NativeUtils'
apply plugin: 'edu.wpi.first.DeployUtils'
apply from: "${rootDir}/shared/config.gradle"
application {
if (OperatingSystem.current().isMacOsX()) {
applicationDefaultJvmArgs = ['-XstartOnFirstThread']
}
}
ext {
sharedCvConfigs = [benchmarkCpp: []]
staticCvConfigs = [benchmarkCppStatic: []]
useJava = true
useCpp = true
skipDev = true
}
apply from: "${rootDir}/shared/opencv.gradle"
application {
mainClass = 'frc.robot.Main'
}
apply plugin: 'com.gradleup.shadow'
repositories {
maven {
url = 'https://frcmaven.wpi.edu/artifactory/ex-mvn'
}
}
dependencies {
implementation project(':apriltag')
implementation project(':cameraserver')
implementation project(':cscore')
implementation project(':epilogue-runtime')
implementation project(':hal')
implementation project(':ntcore')
implementation project(':wpilibj')
implementation project(':wpilibNewCommands')
implementation project(':wpimath')
implementation project(':wpinet')
implementation project(':wpiunits')
implementation project(':wpiutil')
annotationProcessor project(':epilogue-processor')
implementation "org.openjdk.jmh:jmh-core:1.37"
annotationProcessor "org.openjdk.jmh:jmh-generator-annprocess:1.37"
}
tasks.withType(com.github.spotbugs.snom.SpotBugsTask).configureEach {
onlyIf { false }
}
deploy {
targets {
roborio(RemoteTarget) {
directory = '/home/lvuser'
maxChannels = 4
locations {
ssh(SshDeployLocation) {
address = "172.22.11.2"
user = 'admin'
password = ''
ipv6 = false
}
}
def remote = it
artifacts.registerFactory(WPIJREArtifact) {
return objects.newInstance(WPIJREArtifact, it, remote)
}
artifacts {
all {
predeploy << { ctx ->
ctx.execute('. /etc/profile.d/natinst-path.sh; /usr/local/frc/bin/frcKillRobot.sh -t 2> /dev/null')
ctx.execute("sed -i -e 's/\"exec /\"/' /usr/local/frc/bin/frcRunRobot.sh")
}
postdeploy << { ctx ->
ctx.execute("sync")
ctx.execute("ldconfig")
}
}
benchmarkCpp(NativeExecutableArtifact) {
libraryDirectory = '/usr/local/frc/third-party/lib'
def excludes = getLibraryFilter().getExcludes()
excludes.add('**/*.so.debug')
excludes.add('**/*.so.*.debug')
postdeploy << { ctx ->
ctx.execute("echo '/home/lvuser/benchmarkCpp' > /home/lvuser/robotCommand")
ctx.execute("chmod +x /home/lvuser/robotCommand; chown lvuser /home/lvuser/robotCommand")
ctx.execute("setcap cap_sys_nice+eip \"/home/lvuser/benchmarkCpp\"")
ctx.execute('chmod +x benchmarkCpp')
}
}
benchmarkCppStatic(NativeExecutableArtifact) {
libraryDirectory = '/usr/local/frc/third-party/lib'
postdeploy << { ctx ->
ctx.execute("echo '/home/lvuser/benchmarkCppStatic' > /home/lvuser/robotCommand")
ctx.execute("chmod +x /home/lvuser/robotCommand; chown lvuser /home/lvuser/robotCommand")
ctx.execute("setcap cap_sys_nice+eip \"/home/lvuser/benchmarkCppStatic\"")
ctx.execute('chmod +x benchmarkCppStatic')
}
}
benchmarkCppJava(NativeExecutableArtifact) {
libraryDirectory = '/usr/local/frc/third-party/lib'
def excludes = getLibraryFilter().getExcludes()
excludes.add('**/*.so.debug')
excludes.add('**/*.so.*.debug')
}
jre(WPIJREArtifact) {
}
benchmarkJava(JavaArtifact) {
jarTask = shadowJar
postdeploy << { ctx ->
ctx.execute("echo '/usr/local/frc/JRE/bin/java -XX:+UseSerialGC -Djava.library.path=/usr/local/frc/third-party/lib -Djava.lang.invoke.stringConcat=BC_SB -jar /home/lvuser/benchmark-all.jar' > /home/lvuser/robotCommand")
ctx.execute("chmod +x /home/lvuser/robotCommand; chown lvuser /home/lvuser/robotCommand")
}
}
}
}
}
}
// Prevent the eclipse compiler (used by the VS Code extension for intellisense and debugging)
// from generating bad class files from annotation processors like Epilogue
eclipse {
classpath {
containers 'org.eclipse.buildship.core.gradleclasspathcontainer'
file.whenMerged { cp ->
def entries = cp.entries;
def src = new org.gradle.plugins.ide.eclipse.model.SourceFolder('build/generated/sources/annotationProcessor/java/main/', null)
entries.add(src)
}
}
}
tasks.register('deployJava') {
try {
dependsOn tasks.named('deployjreroborio')
dependsOn tasks.named('deploybenchmarkJavaroborio')
dependsOn tasks.named('deploybenchmarkCppJavaroborio') // Deploying shared C++ is how to get the Java shared libraries.
} catch (ignored) {
}
}
tasks.register('deployShared') {
try {
dependsOn tasks.named('deploybenchmarkCpproborio')
} catch (ignored) {
}
}
tasks.register('deployStatic') {
try {
dependsOn tasks.named('deploybenchmarkCppStaticroborio')
} catch (ignored) {
}
}
model {
components {
benchmarkCpp(NativeExecutableSpec) {
if (project.hasProperty('ciDebugOnly')) {
targetBuildTypes 'debug'
} else {
targetBuildTypes 'release'
}
sources {
cpp {
source {
srcDirs = [
'src/main/native/cpp',
'src/main/native/thirdparty/benchmark/src'
]
includes = ['**/*.cpp']
}
exportedHeaders {
srcDirs = [
'src/main/native/include',
'src/main/native/thirdparty/benchmark/include',
'src/main/native/thirdparty/benchmark/src'
]
includes = ['**/*.h']
}
}
}
binaries.all { binary ->
if (binary.targetPlatform.name == nativeUtils.wpi.platforms.roborio) {
if (binary.buildType.name == 'debug') {
deploy.targets.roborio.artifacts.benchmarkCpp.binary = binary
deploy.targets.roborio.artifacts.benchmarkCppJava.binary = binary
}
}
lib project: ':apriltag', library: 'apriltag', linkage: 'shared'
lib project: ':cameraserver', library: 'cameraserver', linkage: 'shared'
lib project: ':cscore', library: 'cscore', linkage: 'shared'
lib project: ':cscore', library: 'cscoreJNIShared', linkage: 'shared'
project(':hal').addHalDependency(binary, 'shared')
project(':hal').addHalJniDependency(binary)
project(':ntcore').addNtcoreDependency(binary, 'shared')
project(':ntcore').addNtcoreJniDependency(binary)
lib project: ':wpilibc', library: 'wpilibc', linkage: 'shared'
lib project: ':wpilibNewCommands', library: 'wpilibNewCommands', linkage: 'shared'
lib project: ':wpimath', library: 'wpimath', linkage: 'shared'
lib project: ':wpimath', library: 'wpimathJNIShared', linkage: 'shared'
lib project: ':wpinet', library: 'wpinet', linkage: 'shared'
lib project: ':wpinet', library: 'wpinetJNIShared', linkage: 'shared'
lib project: ':wpiutil', library: 'wpiutil', linkage: 'shared'
lib project: ':wpiutil', library: 'wpiutilJNIShared', linkage: 'shared'
if (binary.targetPlatform.name == nativeUtils.wpi.platforms.roborio) {
nativeUtils.useRequiredLibrary(binary, 'ni_link_libraries', 'ni_runtime_libraries')
}
if (binary.targetPlatform.operatingSystem.isWindows()) {
// Shlwapi.lib is needed for SHGetValueA() inside thirdparty benchmark
binary.linker.args << "Shlwapi.lib"
}
binary.cppCompiler.define 'benchmark_EXPORTS'
}
}
benchmarkCppStatic(NativeExecutableSpec) {
if (project.hasProperty('ciDebugOnly')) {
targetBuildTypes 'debug'
} else {
targetBuildTypes 'release'
}
nativeUtils.excludeBinariesFromStrip(it)
sources {
cpp {
source {
srcDirs = [
'src/main/native/cpp',
'src/main/native/thirdparty/benchmark/src'
]
includes = ['**/*.cpp']
}
exportedHeaders {
srcDirs = [
'src/main/native/include',
'src/main/native/thirdparty/benchmark/include',
'src/main/native/thirdparty/benchmark/src'
]
includes = ['**/*.h']
}
}
}
binaries.all { binary ->
if (binary.targetPlatform.name == nativeUtils.wpi.platforms.roborio) {
if (binary.buildType.name == 'debug') {
deploy.targets.roborio.artifacts.benchmarkCppStatic.binary = binary
}
}
lib project: ':apriltag', library: 'apriltag', linkage: 'static'
lib project: ':cameraserver', library: 'cameraserver', linkage: 'static'
lib project: ':cscore', library: 'cscore', linkage: 'static'
project(':hal').addHalDependency(binary, 'static')
project(':ntcore').addNtcoreDependency(binary, 'static')
lib project: ':wpilibc', library: 'wpilibc', linkage: 'static'
lib project: ':wpilibNewCommands', library: 'wpilibNewCommands', linkage: 'static'
lib project: ':wpimath', library: 'wpimath', linkage: 'static'
lib project: ':wpinet', library: 'wpinet', linkage: 'static'
lib project: ':wpiutil', library: 'wpiutil', linkage: 'static'
if (binary.targetPlatform.name == nativeUtils.wpi.platforms.roborio) {
nativeUtils.useRequiredLibrary(binary, 'ni_link_libraries', 'ni_runtime_libraries')
}
if (binary.targetPlatform.operatingSystem.isWindows()) {
// Shlwapi.lib is needed for SHGetValueA() inside thirdparty benchmark
binary.linker.args << "Shlwapi.lib"
}
binary.cppCompiler.define 'benchmark_EXPORTS'
binary.cppCompiler.define 'BENCHMARK_STATIC_DEFINE'
}
}
all {
it.sources.each {
it.exportedHeaders {
srcDirs 'src/main/native/thirdparty/benchmark/include'
}
}
}
}
tasks {
def c = $.components
project.tasks.create('runCpp', Exec) {
group = 'WPILib'
description = "Run the benchmarkCpp executable"
def found = false
def systemArch = getCurrentArch()
c.each {
if (it in NativeExecutableSpec && it.name == "benchmarkCpp") {
it.binaries.each {
if (!found) {
def arch = it.targetPlatform.name
if (arch == systemArch) {
dependsOn it.tasks.install
commandLine it.tasks.install.runScriptFile.get().asFile.toString()
def filePath = it.tasks.install.installDirectory.get().toString() + File.separatorChar + 'lib'
run.dependsOn it.tasks.install
run.systemProperty 'java.library.path', filePath
found = true
}
}
}
}
}
}
installAthena(Task) {
$.binaries.each {
if (it in NativeExecutableBinarySpec && it.targetPlatform.name == nativeUtils.wpi.platforms.roborio && it.component.name == 'benchmarkCpp') {
dependsOn it.tasks.install
}
}
}
installAthenaStatic(Task) {
$.binaries.each {
if (it in NativeExecutableBinarySpec && it.targetPlatform.name == nativeUtils.wpi.platforms.roborio && it.component.name == 'benchmarkCppStatic') {
dependsOn it.tasks.install
}
}
}
}
}

View File

@@ -0,0 +1,79 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
package frc.robot;
import edu.wpi.first.math.geometry.Pose2d;
import edu.wpi.first.math.geometry.Rotation2d;
import edu.wpi.first.math.path.TravelingSalesman;
import java.util.concurrent.TimeUnit;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.profile.GCProfiler;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;
import org.openjdk.jmh.runner.options.TimeValue;
public class Main {
private static final Pose2d[] poses = {
new Pose2d(-1, 1, Rotation2d.kCW_90deg),
new Pose2d(-1, 2, Rotation2d.kCCW_90deg),
new Pose2d(0, 0, Rotation2d.kZero),
new Pose2d(0, 3, Rotation2d.kCW_90deg),
new Pose2d(1, 1, Rotation2d.kCCW_90deg),
new Pose2d(1, 2, Rotation2d.kCCW_90deg),
};
private static final int iterations = 100;
private static final TravelingSalesman transformTraveler =
new TravelingSalesman(
(pose1, pose2) -> {
var transform = pose2.minus(pose1);
return Math.hypot(transform.getX(), transform.getY());
});
private static final TravelingSalesman twistTraveler =
new TravelingSalesman(
(pose1, pose2) -> {
var twist = pose1.log(pose2);
return Math.hypot(twist.dx, twist.dy);
});
/**
* Main function.
*
* @param args The (unused) arguments to the program.
*/
public static void main(String... args) throws RunnerException {
Options opt =
new OptionsBuilder()
.include(Main.class.getSimpleName())
.addProfiler(GCProfiler.class)
.forks(1)
.warmupIterations(2)
.warmupTime(TimeValue.seconds(3))
.measurementIterations(3)
.measurementTime(TimeValue.seconds(3))
.build();
new Runner(opt).run();
}
@Benchmark
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
public Pose2d[] transform() {
return transformTraveler.solve(poses, iterations);
}
@Benchmark
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
public Pose2d[] twist() {
return twistTraveler.solve(poses, iterations);
}
}

View File

@@ -0,0 +1,44 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#include <benchmark/benchmark.h>
#include <frc/geometry/Pose2d.h>
#include <frc/path/TravelingSalesman.h>
#include <units/angle.h>
#include <units/length.h>
#include <wpi/array.h>
static constexpr wpi::array<frc::Pose2d, 6> poses{
frc::Pose2d{-1_m, 1_m, -90_deg}, frc::Pose2d{-1_m, 2_m, 90_deg},
frc::Pose2d{0_m, 0_m, 0_deg}, frc::Pose2d{0_m, 3_m, -90_deg},
frc::Pose2d{1_m, 1_m, 90_deg}, frc::Pose2d{1_m, 2_m, 90_deg},
};
static constexpr int iterations = 100;
void BM_Transform(benchmark::State& state) {
frc::TravelingSalesman traveler{[](auto pose1, auto pose2) {
auto transform = pose2 - pose1;
return units::math::hypot(transform.X(), transform.Y()).value();
}};
// NOLINTNEXTLINE(clang-analyzer-deadcode.DeadStores)
for (auto _ : state) {
traveler.Solve(poses, iterations);
}
}
BENCHMARK(BM_Transform);
void BM_Twist(benchmark::State& state) {
frc::TravelingSalesman traveler{[](auto pose1, auto pose2) {
auto twist = pose1.Log(pose2);
return units::math::hypot(twist.dx, twist.dy).value();
}};
// NOLINTNEXTLINE(clang-analyzer-deadcode.DeadStores)
for (auto _ : state) {
traveler.Solve(poses, iterations);
}
}
BENCHMARK(BM_Twist);
BENCHMARK_MAIN();

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,47 @@
#ifndef BENCHMARK_EXPORT_H
#define BENCHMARK_EXPORT_H
#if defined(_WIN32)
#define EXPORT_ATTR __declspec(dllexport)
#define IMPORT_ATTR __declspec(dllimport)
#define NO_EXPORT_ATTR
#define DEPRECATED_ATTR __declspec(deprecated)
#else // _WIN32
#define EXPORT_ATTR __attribute__((visibility("default")))
#define IMPORT_ATTR __attribute__((visibility("default")))
#define NO_EXPORT_ATTR __attribute__((visibility("hidden")))
#define DEPRECATE_ATTR __attribute__((__deprecated__))
#endif // _WIN32
#ifdef BENCHMARK_STATIC_DEFINE
#define BENCHMARK_EXPORT
#define BENCHMARK_NO_EXPORT
#else // BENCHMARK_STATIC_DEFINE
#ifndef BENCHMARK_EXPORT
#ifdef benchmark_EXPORTS
/* We are building this library */
#define BENCHMARK_EXPORT EXPORT_ATTR
#else // benchmark_EXPORTS
/* We are using this library */
#define BENCHMARK_EXPORT IMPORT_ATTR
#endif // benchmark_EXPORTS
#endif // !BENCHMARK_EXPORT
#ifndef BENCHMARK_NO_EXPORT
#define BENCHMARK_NO_EXPORT NO_EXPORT_ATTR
#endif // !BENCHMARK_NO_EXPORT
#endif // BENCHMARK_STATIC_DEFINE
#ifndef BENCHMARK_DEPRECATED
#define BENCHMARK_DEPRECATED DEPRECATE_ATTR
#endif // BENCHMARK_DEPRECATED
#ifndef BENCHMARK_DEPRECATED_EXPORT
#define BENCHMARK_DEPRECATED_EXPORT BENCHMARK_EXPORT BENCHMARK_DEPRECATED
#endif // BENCHMARK_DEPRECATED_EXPORT
#ifndef BENCHMARK_DEPRECATED_NO_EXPORT
#define BENCHMARK_DEPRECATED_NO_EXPORT BENCHMARK_NO_EXPORT BENCHMARK_DEPRECATED
#endif // BENCHMARK_DEPRECATED_EXPORT
#endif /* BENCHMARK_EXPORT_H */

View File

@@ -0,0 +1,33 @@
#ifndef BENCHMARK_ARRAYSIZE_H_
#define BENCHMARK_ARRAYSIZE_H_
#include "internal_macros.h"
namespace benchmark {
namespace internal {
// The arraysize(arr) macro returns the # of elements in an array arr.
// The expression is a compile-time constant, and therefore can be
// used in defining new arrays, for example. If you use arraysize on
// a pointer by mistake, you will get a compile-time error.
//
// This template function declaration is used in defining arraysize.
// Note that the function doesn't need an implementation, as we only
// use its type.
template <typename T, size_t N>
char (&ArraySizeHelper(T (&array)[N]))[N];
// That gcc wants both of these prototypes seems mysterious. VC, for
// its part, can't decide which to use (another mystery). Matching of
// template overloads: the final frontier.
#ifndef COMPILER_MSVC
template <typename T, size_t N>
char (&ArraySizeHelper(const T (&array)[N]))[N];
#endif
#define arraysize(array) (sizeof(::benchmark::internal::ArraySizeHelper(array)))
} // end namespace internal
} // end namespace benchmark
#endif // BENCHMARK_ARRAYSIZE_H_

View File

@@ -0,0 +1,912 @@
// Copyright 2015 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "benchmark/benchmark.h"
#include "benchmark_api_internal.h"
#include "benchmark_runner.h"
#include "internal_macros.h"
#ifndef BENCHMARK_OS_WINDOWS
#if !defined(BENCHMARK_OS_FUCHSIA) && !defined(BENCHMARK_OS_QURT)
#include <sys/resource.h>
#endif
#include <sys/time.h>
#include <unistd.h>
#endif
#ifdef BENCHMARK_OS_LINUX
#include <sys/personality.h>
#endif
#include <algorithm>
#include <atomic>
#include <condition_variable>
#include <cstdio>
#include <cstdlib>
#include <fstream>
#include <iostream>
#include <limits>
#include <map>
#include <memory>
#include <random>
#include <string>
#include <thread>
#include <utility>
#include "check.h"
#include "colorprint.h"
#include "commandlineflags.h"
#include "complexity.h"
#include "counter.h"
#include "log.h"
#include "mutex.h"
#include "perf_counters.h"
#include "re.h"
#include "statistics.h"
#include "string_util.h"
#include "thread_manager.h"
#include "thread_timer.h"
namespace benchmark {
// Print a list of benchmarks. This option overrides all other options.
BM_DEFINE_bool(benchmark_list_tests, false);
// A regular expression that specifies the set of benchmarks to execute. If
// this flag is empty, or if this flag is the string \"all\", all benchmarks
// linked into the binary are run.
BM_DEFINE_string(benchmark_filter, "");
// Specification of how long to run the benchmark.
//
// It can be either an exact number of iterations (specified as `<integer>x`),
// or a minimum number of seconds (specified as `<float>s`). If the latter
// format (ie., min seconds) is used, the system may run the benchmark longer
// until the results are considered significant.
//
// For backward compatibility, the `s` suffix may be omitted, in which case,
// the specified number is interpreted as the number of seconds.
//
// For cpu-time based tests, this is the lower bound
// on the total cpu time used by all threads that make up the test. For
// real-time based tests, this is the lower bound on the elapsed time of the
// benchmark execution, regardless of number of threads.
BM_DEFINE_string(benchmark_min_time, kDefaultMinTimeStr);
// Minimum number of seconds a benchmark should be run before results should be
// taken into account. This e.g can be necessary for benchmarks of code which
// needs to fill some form of cache before performance is of interest.
// Note: results gathered within this period are discarded and not used for
// reported result.
BM_DEFINE_double(benchmark_min_warmup_time, 0.0);
// The number of runs of each benchmark. If greater than 1, the mean and
// standard deviation of the runs will be reported.
BM_DEFINE_int32(benchmark_repetitions, 1);
// If enabled, forces each benchmark to execute exactly one iteration and one
// repetition, bypassing any configured
// MinTime()/MinWarmUpTime()/Iterations()/Repetitions()
BM_DEFINE_bool(benchmark_dry_run, false);
// If set, enable random interleaving of repetitions of all benchmarks.
// See http://github.com/google/benchmark/issues/1051 for details.
BM_DEFINE_bool(benchmark_enable_random_interleaving, false);
// Report the result of each benchmark repetitions. When 'true' is specified
// only the mean, standard deviation, and other statistics are reported for
// repeated benchmarks. Affects all reporters.
BM_DEFINE_bool(benchmark_report_aggregates_only, false);
// Display the result of each benchmark repetitions. When 'true' is specified
// only the mean, standard deviation, and other statistics are displayed for
// repeated benchmarks. Unlike benchmark_report_aggregates_only, only affects
// the display reporter, but *NOT* file reporter, which will still contain
// all the output.
BM_DEFINE_bool(benchmark_display_aggregates_only, false);
// The format to use for console output.
// Valid values are 'console', 'json', or 'csv'.
BM_DEFINE_string(benchmark_format, "console");
// The format to use for file output.
// Valid values are 'console', 'json', or 'csv'.
BM_DEFINE_string(benchmark_out_format, "json");
// The file to write additional output to.
BM_DEFINE_string(benchmark_out, "");
// Whether to use colors in the output. Valid values:
// 'true'/'yes'/1, 'false'/'no'/0, and 'auto'. 'auto' means to use colors if
// the output is being sent to a terminal and the TERM environment variable is
// set to a terminal type that supports colors.
BM_DEFINE_string(benchmark_color, "auto");
// Whether to use tabular format when printing user counters to the console.
// Valid values: 'true'/'yes'/1, 'false'/'no'/0. Defaults to false.
BM_DEFINE_bool(benchmark_counters_tabular, false);
// List of additional perf counters to collect, in libpfm format. For more
// information about libpfm: https://man7.org/linux/man-pages/man3/libpfm.3.html
BM_DEFINE_string(benchmark_perf_counters, "");
// Extra context to include in the output formatted as comma-separated key-value
// pairs. Kept internal as it's only used for parsing from env/command line.
BM_DEFINE_kvpairs(benchmark_context, {});
// Set the default time unit to use for reports
// Valid values are 'ns', 'us', 'ms' or 's'
BM_DEFINE_string(benchmark_time_unit, "");
// The level of verbose logging to output
BM_DEFINE_int32(v, 0);
namespace internal {
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
std::map<std::string, std::string>* global_context = nullptr;
BENCHMARK_EXPORT std::map<std::string, std::string>*& GetGlobalContext() {
return global_context;
}
namespace {
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
void const volatile* volatile global_force_escape_pointer;
} // namespace
// FIXME: Verify if LTO still messes this up?
void UseCharPointer(char const volatile* const v) {
// We want to escape the pointer `v` so that the compiler can not eliminate
// computations that produced it. To do that, we escape the pointer by storing
// it into a volatile variable, since generally, volatile store, is not
// something the compiler is allowed to elide.
global_force_escape_pointer = reinterpret_cast<void const volatile*>(v);
}
} // namespace internal
State::State(std::string name, IterationCount max_iters,
const std::vector<int64_t>& ranges, int thread_i, int n_threads,
internal::ThreadTimer* timer, internal::ThreadManager* manager,
internal::PerfCountersMeasurement* perf_counters_measurement,
ProfilerManager* profiler_manager)
: total_iterations_(0),
batch_leftover_(0),
max_iterations(max_iters),
started_(false),
finished_(false),
skipped_(internal::NotSkipped),
range_(ranges),
complexity_n_(0),
name_(std::move(name)),
thread_index_(thread_i),
threads_(n_threads),
timer_(timer),
manager_(manager),
perf_counters_measurement_(perf_counters_measurement),
profiler_manager_(profiler_manager) {
BM_CHECK(max_iterations != 0) << "At least one iteration must be run";
BM_CHECK_LT(thread_index_, threads_)
<< "thread_index must be less than threads";
// Add counters with correct flag now. If added with `counters[name]` in
// `PauseTiming`, a new `Counter` will be inserted the first time, which
// won't have the flag. Inserting them now also reduces the allocations
// during the benchmark.
if (perf_counters_measurement_ != nullptr) {
for (const std::string& counter_name :
perf_counters_measurement_->names()) {
counters[counter_name] = Counter(0.0, Counter::kAvgIterations);
}
}
// Note: The use of offsetof below is technically undefined until C++17
// because State is not a standard layout type. However, all compilers
// currently provide well-defined behavior as an extension (which is
// demonstrated since constexpr evaluation must diagnose all undefined
// behavior). However, GCC and Clang also warn about this use of offsetof,
// which must be suppressed.
#if defined(__INTEL_COMPILER)
#pragma warning push
#pragma warning(disable : 1875)
#elif defined(__GNUC__) || defined(__clang__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Winvalid-offsetof"
#endif
#if defined(__NVCC__)
#pragma nv_diagnostic push
#pragma nv_diag_suppress 1427
#endif
#if defined(__NVCOMPILER)
#pragma diagnostic push
#pragma diag_suppress offset_in_non_POD_nonstandard
#endif
// Offset tests to ensure commonly accessed data is on the first cache line.
const int cache_line_size = 64;
static_assert(
offsetof(State, skipped_) <= (cache_line_size - sizeof(skipped_)), "");
#if defined(__INTEL_COMPILER)
#pragma warning pop
#elif defined(__GNUC__) || defined(__clang__)
#pragma GCC diagnostic pop
#endif
#if defined(__NVCC__)
#pragma nv_diagnostic pop
#endif
#if defined(__NVCOMPILER)
#pragma diagnostic pop
#endif
}
void State::PauseTiming() {
// Add in time accumulated so far
BM_CHECK(started_ && !finished_ && !skipped());
timer_->StopTimer();
if (perf_counters_measurement_ != nullptr) {
std::vector<std::pair<std::string, double>> measurements;
if (!perf_counters_measurement_->Stop(measurements)) {
BM_CHECK(false) << "Perf counters read the value failed.";
}
for (const auto& name_and_measurement : measurements) {
const std::string& name = name_and_measurement.first;
const double measurement = name_and_measurement.second;
// Counter was inserted with `kAvgIterations` flag by the constructor.
assert(counters.find(name) != counters.end());
counters[name].value += measurement;
}
}
}
void State::ResumeTiming() {
BM_CHECK(started_ && !finished_ && !skipped());
timer_->StartTimer();
if (perf_counters_measurement_ != nullptr) {
perf_counters_measurement_->Start();
}
}
void State::SkipWithMessage(const std::string& msg) {
skipped_ = internal::SkippedWithMessage;
{
MutexLock l(manager_->GetBenchmarkMutex());
if (internal::NotSkipped == manager_->results.skipped_) {
manager_->results.skip_message_ = msg;
manager_->results.skipped_ = skipped_;
}
}
total_iterations_ = 0;
if (timer_->running()) {
timer_->StopTimer();
}
}
void State::SkipWithError(const std::string& msg) {
skipped_ = internal::SkippedWithError;
{
MutexLock l(manager_->GetBenchmarkMutex());
if (internal::NotSkipped == manager_->results.skipped_) {
manager_->results.skip_message_ = msg;
manager_->results.skipped_ = skipped_;
}
}
total_iterations_ = 0;
if (timer_->running()) {
timer_->StopTimer();
}
}
void State::SetIterationTime(double seconds) {
timer_->SetIterationTime(seconds);
}
void State::SetLabel(const std::string& label) {
MutexLock l(manager_->GetBenchmarkMutex());
manager_->results.report_label_ = label;
}
void State::StartKeepRunning() {
BM_CHECK(!started_ && !finished_);
started_ = true;
total_iterations_ = skipped() ? 0 : max_iterations;
if (BENCHMARK_BUILTIN_EXPECT(profiler_manager_ != nullptr, false)) {
profiler_manager_->AfterSetupStart();
}
manager_->StartStopBarrier();
if (!skipped()) {
ResumeTiming();
}
}
void State::FinishKeepRunning() {
BM_CHECK(started_ && (!finished_ || skipped()));
if (!skipped()) {
PauseTiming();
}
// Total iterations has now wrapped around past 0. Fix this.
total_iterations_ = 0;
finished_ = true;
manager_->StartStopBarrier();
if (BENCHMARK_BUILTIN_EXPECT(profiler_manager_ != nullptr, false)) {
profiler_manager_->BeforeTeardownStop();
}
}
namespace internal {
namespace {
// Flushes streams after invoking reporter methods that write to them. This
// ensures users get timely updates even when streams are not line-buffered.
void FlushStreams(BenchmarkReporter* reporter) {
if (reporter == nullptr) {
return;
}
std::flush(reporter->GetOutputStream());
std::flush(reporter->GetErrorStream());
}
// Reports in both display and file reporters.
void Report(BenchmarkReporter* display_reporter,
BenchmarkReporter* file_reporter, const RunResults& run_results) {
auto report_one = [](BenchmarkReporter* reporter, bool aggregates_only,
const RunResults& results) {
assert(reporter);
// If there are no aggregates, do output non-aggregates.
aggregates_only &= !results.aggregates_only.empty();
if (!aggregates_only) {
reporter->ReportRuns(results.non_aggregates);
}
if (!results.aggregates_only.empty()) {
reporter->ReportRuns(results.aggregates_only);
}
};
report_one(display_reporter, run_results.display_report_aggregates_only,
run_results);
if (file_reporter != nullptr) {
report_one(file_reporter, run_results.file_report_aggregates_only,
run_results);
}
FlushStreams(display_reporter);
FlushStreams(file_reporter);
}
void RunBenchmarks(const std::vector<BenchmarkInstance>& benchmarks,
BenchmarkReporter* display_reporter,
BenchmarkReporter* file_reporter) {
// Note the file_reporter can be null.
BM_CHECK(display_reporter != nullptr);
// Determine the width of the name field using a minimum width of 10.
bool might_have_aggregates = FLAGS_benchmark_repetitions > 1;
size_t name_field_width = 10;
size_t stat_field_width = 0;
for (const BenchmarkInstance& benchmark : benchmarks) {
name_field_width =
std::max<size_t>(name_field_width, benchmark.name().str().size());
might_have_aggregates |= benchmark.repetitions() > 1;
for (const auto& Stat : benchmark.statistics()) {
stat_field_width = std::max<size_t>(stat_field_width, Stat.name_.size());
}
}
if (might_have_aggregates) {
name_field_width += 1 + stat_field_width;
}
// Print header here
BenchmarkReporter::Context context;
context.name_field_width = name_field_width;
// Keep track of running times of all instances of each benchmark family.
std::map<int /*family_index*/, BenchmarkReporter::PerFamilyRunReports>
per_family_reports;
if (display_reporter->ReportContext(context) &&
((file_reporter == nullptr) || file_reporter->ReportContext(context))) {
FlushStreams(display_reporter);
FlushStreams(file_reporter);
size_t num_repetitions_total = 0;
// This perfcounters object needs to be created before the runners vector
// below so it outlasts their lifetime.
PerfCountersMeasurement perfcounters(
StrSplit(FLAGS_benchmark_perf_counters, ','));
// Vector of benchmarks to run
std::vector<internal::BenchmarkRunner> runners;
runners.reserve(benchmarks.size());
// Count the number of benchmarks with threads to warn the user in case
// performance counters are used.
int benchmarks_with_threads = 0;
// Loop through all benchmarks
for (const BenchmarkInstance& benchmark : benchmarks) {
BenchmarkReporter::PerFamilyRunReports* reports_for_family = nullptr;
if (benchmark.complexity() != oNone) {
reports_for_family = &per_family_reports[benchmark.family_index()];
}
benchmarks_with_threads += static_cast<int>(benchmark.threads() > 1);
runners.emplace_back(benchmark, &perfcounters, reports_for_family);
int num_repeats_of_this_instance = runners.back().GetNumRepeats();
num_repetitions_total +=
static_cast<size_t>(num_repeats_of_this_instance);
if (reports_for_family != nullptr) {
reports_for_family->num_runs_total += num_repeats_of_this_instance;
}
}
assert(runners.size() == benchmarks.size() && "Unexpected runner count.");
// The use of performance counters with threads would be unintuitive for
// the average user so we need to warn them about this case
if ((benchmarks_with_threads > 0) && (perfcounters.num_counters() > 0)) {
GetErrorLogInstance()
<< "***WARNING*** There are " << benchmarks_with_threads
<< " benchmarks with threads and " << perfcounters.num_counters()
<< " performance counters were requested. Beware counters will "
"reflect the combined usage across all "
"threads.\n";
}
std::vector<size_t> repetition_indices;
repetition_indices.reserve(num_repetitions_total);
for (size_t runner_index = 0, num_runners = runners.size();
runner_index != num_runners; ++runner_index) {
const internal::BenchmarkRunner& runner = runners[runner_index];
std::fill_n(std::back_inserter(repetition_indices),
runner.GetNumRepeats(), runner_index);
}
assert(repetition_indices.size() == num_repetitions_total &&
"Unexpected number of repetition indexes.");
if (FLAGS_benchmark_enable_random_interleaving) {
std::random_device rd;
std::mt19937 g(rd());
std::shuffle(repetition_indices.begin(), repetition_indices.end(), g);
}
for (size_t repetition_index : repetition_indices) {
internal::BenchmarkRunner& runner = runners[repetition_index];
runner.DoOneRepetition();
if (runner.HasRepeatsRemaining()) {
continue;
}
// FIXME: report each repetition separately, not all of them in bulk.
display_reporter->ReportRunsConfig(
runner.GetMinTime(), runner.HasExplicitIters(), runner.GetIters());
if (file_reporter != nullptr) {
file_reporter->ReportRunsConfig(
runner.GetMinTime(), runner.HasExplicitIters(), runner.GetIters());
}
RunResults run_results = runner.GetResults();
// Maybe calculate complexity report
if (const auto* reports_for_family = runner.GetReportsForFamily()) {
if (reports_for_family->num_runs_done ==
reports_for_family->num_runs_total) {
auto additional_run_stats = ComputeBigO(reports_for_family->Runs);
run_results.aggregates_only.insert(run_results.aggregates_only.end(),
additional_run_stats.begin(),
additional_run_stats.end());
per_family_reports.erase(
static_cast<int>(reports_for_family->Runs.front().family_index));
}
}
Report(display_reporter, file_reporter, run_results);
}
}
display_reporter->Finalize();
if (file_reporter != nullptr) {
file_reporter->Finalize();
}
FlushStreams(display_reporter);
FlushStreams(file_reporter);
}
// Disable deprecated warnings temporarily because we need to reference
// CSVReporter but don't want to trigger -Werror=-Wdeprecated-declarations
BENCHMARK_DISABLE_DEPRECATED_WARNING
std::unique_ptr<BenchmarkReporter> CreateReporter(
std::string const& name, ConsoleReporter::OutputOptions output_opts) {
typedef std::unique_ptr<BenchmarkReporter> PtrType;
if (name == "console") {
return PtrType(new ConsoleReporter(output_opts));
}
if (name == "json") {
return PtrType(new JSONReporter());
}
if (name == "csv") {
return PtrType(new CSVReporter());
}
std::cerr << "Unexpected format: '" << name << "'\n";
std::flush(std::cerr);
std::exit(1);
}
BENCHMARK_RESTORE_DEPRECATED_WARNING
} // end namespace
bool IsZero(double n) {
return std::abs(n) < std::numeric_limits<double>::epsilon();
}
ConsoleReporter::OutputOptions GetOutputOptions(bool force_no_color) {
int output_opts = ConsoleReporter::OO_Defaults;
auto is_benchmark_color = [force_no_color]() -> bool {
if (force_no_color) {
return false;
}
if (FLAGS_benchmark_color == "auto") {
return IsColorTerminal();
}
return IsTruthyFlagValue(FLAGS_benchmark_color);
};
if (is_benchmark_color()) {
output_opts |= ConsoleReporter::OO_Color;
} else {
output_opts &= ~ConsoleReporter::OO_Color;
}
if (FLAGS_benchmark_counters_tabular) {
output_opts |= ConsoleReporter::OO_Tabular;
} else {
output_opts &= ~ConsoleReporter::OO_Tabular;
}
return static_cast<ConsoleReporter::OutputOptions>(output_opts);
}
} // end namespace internal
BenchmarkReporter* CreateDefaultDisplayReporter() {
static auto* default_display_reporter =
internal::CreateReporter(FLAGS_benchmark_format,
internal::GetOutputOptions())
.release();
return default_display_reporter;
}
size_t RunSpecifiedBenchmarks() {
return RunSpecifiedBenchmarks(nullptr, nullptr, FLAGS_benchmark_filter);
}
size_t RunSpecifiedBenchmarks(std::string spec) {
return RunSpecifiedBenchmarks(nullptr, nullptr, std::move(spec));
}
size_t RunSpecifiedBenchmarks(BenchmarkReporter* display_reporter) {
return RunSpecifiedBenchmarks(display_reporter, nullptr,
FLAGS_benchmark_filter);
}
size_t RunSpecifiedBenchmarks(BenchmarkReporter* display_reporter,
std::string spec) {
return RunSpecifiedBenchmarks(display_reporter, nullptr, std::move(spec));
}
size_t RunSpecifiedBenchmarks(BenchmarkReporter* display_reporter,
BenchmarkReporter* file_reporter) {
return RunSpecifiedBenchmarks(display_reporter, file_reporter,
FLAGS_benchmark_filter);
}
size_t RunSpecifiedBenchmarks(BenchmarkReporter* display_reporter,
BenchmarkReporter* file_reporter,
std::string spec) {
if (spec.empty() || spec == "all") {
spec = "."; // Regexp that matches all benchmarks
}
// Setup the reporters
std::ofstream output_file;
std::unique_ptr<BenchmarkReporter> default_display_reporter;
std::unique_ptr<BenchmarkReporter> default_file_reporter;
if (display_reporter == nullptr) {
default_display_reporter.reset(CreateDefaultDisplayReporter());
display_reporter = default_display_reporter.get();
}
auto& Out = display_reporter->GetOutputStream();
auto& Err = display_reporter->GetErrorStream();
std::string const& fname = FLAGS_benchmark_out;
if (fname.empty() && (file_reporter != nullptr)) {
Err << "A custom file reporter was provided but "
"--benchmark_out=<file> was not specified.\n";
Out.flush();
Err.flush();
std::exit(1);
}
if (!fname.empty()) {
output_file.open(fname);
if (!output_file.is_open()) {
Err << "invalid file name: '" << fname << "'\n";
Out.flush();
Err.flush();
std::exit(1);
}
if (file_reporter == nullptr) {
default_file_reporter = internal::CreateReporter(
FLAGS_benchmark_out_format, FLAGS_benchmark_counters_tabular
? ConsoleReporter::OO_Tabular
: ConsoleReporter::OO_None);
file_reporter = default_file_reporter.get();
}
file_reporter->SetOutputStream(&output_file);
file_reporter->SetErrorStream(&output_file);
}
std::vector<internal::BenchmarkInstance> benchmarks;
if (!FindBenchmarksInternal(spec, &benchmarks, &Err)) {
Out.flush();
Err.flush();
return 0;
}
if (benchmarks.empty()) {
Err << "Failed to match any benchmarks against regex: " << spec << "\n";
Out.flush();
Err.flush();
return 0;
}
if (FLAGS_benchmark_list_tests) {
for (auto const& benchmark : benchmarks) {
Out << benchmark.name().str() << "\n";
}
} else {
internal::RunBenchmarks(benchmarks, display_reporter, file_reporter);
}
Out.flush();
Err.flush();
return benchmarks.size();
}
namespace {
// stores the time unit benchmarks use by default
TimeUnit default_time_unit = kNanosecond;
} // namespace
TimeUnit GetDefaultTimeUnit() { return default_time_unit; }
void SetDefaultTimeUnit(TimeUnit unit) { default_time_unit = unit; }
std::string GetBenchmarkFilter() { return FLAGS_benchmark_filter; }
void SetBenchmarkFilter(std::string value) {
FLAGS_benchmark_filter = std::move(value);
}
int32_t GetBenchmarkVerbosity() { return FLAGS_v; }
void RegisterMemoryManager(MemoryManager* manager) {
internal::memory_manager = manager;
}
void RegisterProfilerManager(ProfilerManager* manager) {
// Don't allow overwriting an existing manager.
if (manager != nullptr) {
BM_CHECK_EQ(internal::profiler_manager, nullptr);
}
internal::profiler_manager = manager;
}
void AddCustomContext(const std::string& key, const std::string& value) {
if (internal::global_context == nullptr) {
internal::global_context = new std::map<std::string, std::string>();
}
if (!internal::global_context->emplace(key, value).second) {
std::cerr << "Failed to add custom context \"" << key << "\" as it already "
<< "exists with value \"" << value << "\"\n";
}
}
namespace internal {
void (*HelperPrintf)();
void PrintUsageAndExit() {
HelperPrintf();
std::flush(std::cout);
std::flush(std::cerr);
std::exit(0);
}
void SetDefaultTimeUnitFromFlag(const std::string& time_unit_flag) {
if (time_unit_flag == "s") {
return SetDefaultTimeUnit(kSecond);
}
if (time_unit_flag == "ms") {
return SetDefaultTimeUnit(kMillisecond);
}
if (time_unit_flag == "us") {
return SetDefaultTimeUnit(kMicrosecond);
}
if (time_unit_flag == "ns") {
return SetDefaultTimeUnit(kNanosecond);
}
if (!time_unit_flag.empty()) {
PrintUsageAndExit();
}
}
void ParseCommandLineFlags(int* argc, char** argv) {
using namespace benchmark;
BenchmarkReporter::Context::executable_name =
((argc != nullptr) && *argc > 0) ? argv[0] : "unknown";
for (int i = 1; (argc != nullptr) && i < *argc; ++i) {
if (ParseBoolFlag(argv[i], "benchmark_list_tests",
&FLAGS_benchmark_list_tests) ||
ParseStringFlag(argv[i], "benchmark_filter", &FLAGS_benchmark_filter) ||
ParseStringFlag(argv[i], "benchmark_min_time",
&FLAGS_benchmark_min_time) ||
ParseDoubleFlag(argv[i], "benchmark_min_warmup_time",
&FLAGS_benchmark_min_warmup_time) ||
ParseInt32Flag(argv[i], "benchmark_repetitions",
&FLAGS_benchmark_repetitions) ||
ParseBoolFlag(argv[i], "benchmark_dry_run", &FLAGS_benchmark_dry_run) ||
ParseBoolFlag(argv[i], "benchmark_enable_random_interleaving",
&FLAGS_benchmark_enable_random_interleaving) ||
ParseBoolFlag(argv[i], "benchmark_report_aggregates_only",
&FLAGS_benchmark_report_aggregates_only) ||
ParseBoolFlag(argv[i], "benchmark_display_aggregates_only",
&FLAGS_benchmark_display_aggregates_only) ||
ParseStringFlag(argv[i], "benchmark_format", &FLAGS_benchmark_format) ||
ParseStringFlag(argv[i], "benchmark_out", &FLAGS_benchmark_out) ||
ParseStringFlag(argv[i], "benchmark_out_format",
&FLAGS_benchmark_out_format) ||
ParseStringFlag(argv[i], "benchmark_color", &FLAGS_benchmark_color) ||
ParseBoolFlag(argv[i], "benchmark_counters_tabular",
&FLAGS_benchmark_counters_tabular) ||
ParseStringFlag(argv[i], "benchmark_perf_counters",
&FLAGS_benchmark_perf_counters) ||
ParseKeyValueFlag(argv[i], "benchmark_context",
&FLAGS_benchmark_context) ||
ParseStringFlag(argv[i], "benchmark_time_unit",
&FLAGS_benchmark_time_unit) ||
ParseInt32Flag(argv[i], "v", &FLAGS_v)) {
for (int j = i; j != *argc - 1; ++j) {
argv[j] = argv[j + 1];
}
--(*argc);
--i;
} else if (IsFlag(argv[i], "help")) {
PrintUsageAndExit();
}
}
for (auto const* flag :
{&FLAGS_benchmark_format, &FLAGS_benchmark_out_format}) {
if (*flag != "console" && *flag != "json" && *flag != "csv") {
PrintUsageAndExit();
}
}
SetDefaultTimeUnitFromFlag(FLAGS_benchmark_time_unit);
if (FLAGS_benchmark_color.empty()) {
PrintUsageAndExit();
}
if (FLAGS_benchmark_dry_run) {
AddCustomContext("dry_run", "true");
}
for (const auto& kv : FLAGS_benchmark_context) {
AddCustomContext(kv.first, kv.second);
}
}
int InitializeStreams() {
static std::ios_base::Init init;
return 0;
}
template <typename T>
std::make_unsigned_t<T> get_as_unsigned(T v) {
using UnsignedT = std::make_unsigned_t<T>;
return static_cast<UnsignedT>(v);
}
} // end namespace internal
void MaybeReenterWithoutASLR(int /*argc*/, char** argv) {
// On e.g. Hexagon simulator, argv may be NULL.
if (!argv) return;
#ifdef BENCHMARK_OS_LINUX
const auto curr_personality = personality(0xffffffff);
// We should never fail to read-only query the current personality,
// but let's be cautious.
if (curr_personality == -1) return;
// If ASLR is already disabled, we have nothing more to do.
if (internal::get_as_unsigned(curr_personality) & ADDR_NO_RANDOMIZE) return;
// Try to change the personality to disable ASLR.
const auto proposed_personality =
internal::get_as_unsigned(curr_personality) | ADDR_NO_RANDOMIZE;
const auto prev_personality = personality(proposed_personality);
// Have we failed to change the personality? That may happen.
if (prev_personality == -1) return;
// Make sure the parsona has been updated with the no-ASLR flag,
// otherwise we will try to reenter infinitely.
// This seems impossible, but can happen in some docker configurations.
const auto new_personality = personality(0xffffffff);
if ((internal::get_as_unsigned(new_personality) & ADDR_NO_RANDOMIZE) == 0)
return;
execv(argv[0], argv);
// The exec() functions return only if an error has occurred,
// in which case we want to just continue as-is.
#else
return;
#endif
}
std::string GetBenchmarkVersion() {
#ifdef BENCHMARK_VERSION
return {BENCHMARK_VERSION};
#else
return {""};
#endif
}
void PrintDefaultHelp() {
fprintf(stdout,
"benchmark"
" [--benchmark_list_tests={true|false}]\n"
" [--benchmark_filter=<regex>]\n"
" [--benchmark_min_time=`<integer>x` OR `<float>s` ]\n"
" [--benchmark_min_warmup_time=<min_warmup_time>]\n"
" [--benchmark_repetitions=<num_repetitions>]\n"
" [--benchmark_dry_run={true|false}]\n"
" [--benchmark_enable_random_interleaving={true|false}]\n"
" [--benchmark_report_aggregates_only={true|false}]\n"
" [--benchmark_display_aggregates_only={true|false}]\n"
" [--benchmark_format=<console|json|csv>]\n"
" [--benchmark_out=<filename>]\n"
" [--benchmark_out_format=<json|console|csv>]\n"
" [--benchmark_color={auto|true|false}]\n"
" [--benchmark_counters_tabular={true|false}]\n"
#if defined HAVE_LIBPFM
" [--benchmark_perf_counters=<counter>,...]\n"
#endif
" [--benchmark_context=<key>=<value>,...]\n"
" [--benchmark_time_unit={ns|us|ms|s}]\n"
" [--v=<verbosity>]\n");
}
void Initialize(int* argc, char** argv, void (*HelperPrintf)()) {
internal::HelperPrintf = HelperPrintf;
internal::ParseCommandLineFlags(argc, argv);
internal::LogLevel() = FLAGS_v;
}
void Shutdown() { delete internal::global_context; }
bool ReportUnrecognizedArguments(int argc, char** argv) {
for (int i = 1; i < argc; ++i) {
fprintf(stderr, "%s: error: unrecognized command-line flag: %s\n", argv[0],
argv[i]);
}
return argc > 1;
}
} // end namespace benchmark

View File

@@ -0,0 +1,118 @@
#include "benchmark_api_internal.h"
#include <cinttypes>
#include "string_util.h"
namespace benchmark {
namespace internal {
BenchmarkInstance::BenchmarkInstance(Benchmark* benchmark, int family_idx,
int per_family_instance_idx,
const std::vector<int64_t>& args,
int thread_count)
: benchmark_(*benchmark),
family_index_(family_idx),
per_family_instance_index_(per_family_instance_idx),
aggregation_report_mode_(benchmark_.aggregation_report_mode_),
args_(args),
time_unit_(benchmark_.GetTimeUnit()),
measure_process_cpu_time_(benchmark_.measure_process_cpu_time_),
use_real_time_(benchmark_.use_real_time_),
use_manual_time_(benchmark_.use_manual_time_),
complexity_(benchmark_.complexity_),
complexity_lambda_(benchmark_.complexity_lambda_),
statistics_(benchmark_.statistics_),
repetitions_(benchmark_.repetitions_),
min_time_(benchmark_.min_time_),
min_warmup_time_(benchmark_.min_warmup_time_),
iterations_(benchmark_.iterations_),
threads_(thread_count),
setup_(benchmark_.setup_),
teardown_(benchmark_.teardown_) {
name_.function_name = benchmark_.name_;
size_t arg_i = 0;
for (const auto& arg : args) {
if (!name_.args.empty()) {
name_.args += '/';
}
if (arg_i < benchmark->arg_names_.size()) {
const auto& arg_name = benchmark_.arg_names_[arg_i];
if (!arg_name.empty()) {
name_.args += StrFormat("%s:", arg_name.c_str());
}
}
name_.args += StrFormat("%" PRId64, arg);
++arg_i;
}
if (!IsZero(benchmark->min_time_)) {
name_.min_time = StrFormat("min_time:%0.3f", benchmark_.min_time_);
}
if (!IsZero(benchmark->min_warmup_time_)) {
name_.min_warmup_time =
StrFormat("min_warmup_time:%0.3f", benchmark_.min_warmup_time_);
}
if (benchmark_.iterations_ != 0) {
name_.iterations = StrFormat(
"iterations:%lu", static_cast<unsigned long>(benchmark_.iterations_));
}
if (benchmark_.repetitions_ != 0) {
name_.repetitions = StrFormat("repeats:%d", benchmark_.repetitions_);
}
if (benchmark_.measure_process_cpu_time_) {
name_.time_type = "process_time";
}
if (benchmark_.use_manual_time_) {
if (!name_.time_type.empty()) {
name_.time_type += '/';
}
name_.time_type += "manual_time";
} else if (benchmark_.use_real_time_) {
if (!name_.time_type.empty()) {
name_.time_type += '/';
}
name_.time_type += "real_time";
}
if (!benchmark_.thread_counts_.empty()) {
name_.threads = StrFormat("threads:%d", threads_);
}
}
State BenchmarkInstance::Run(
IterationCount iters, int thread_id, internal::ThreadTimer* timer,
internal::ThreadManager* manager,
internal::PerfCountersMeasurement* perf_counters_measurement,
ProfilerManager* profiler_manager) const {
State st(name_.function_name, iters, args_, thread_id, threads_, timer,
manager, perf_counters_measurement, profiler_manager);
benchmark_.Run(st);
return st;
}
void BenchmarkInstance::Setup() const {
if (setup_ != nullptr) {
State st(name_.function_name, /*iters*/ 1, args_, /*thread_id*/ 0, threads_,
nullptr, nullptr, nullptr, nullptr);
setup_(st);
}
}
void BenchmarkInstance::Teardown() const {
if (teardown_ != nullptr) {
State st(name_.function_name, /*iters*/ 1, args_, /*thread_id*/ 0, threads_,
nullptr, nullptr, nullptr, nullptr);
teardown_(st);
}
}
} // namespace internal
} // namespace benchmark

View File

@@ -0,0 +1,90 @@
#ifndef BENCHMARK_API_INTERNAL_H
#define BENCHMARK_API_INTERNAL_H
#include <cmath>
#include <iosfwd>
#include <limits>
#include <memory>
#include <string>
#include <vector>
#include "benchmark/benchmark.h"
#include "commandlineflags.h"
namespace benchmark {
namespace internal {
// Information kept per benchmark we may want to run
class BenchmarkInstance {
public:
BenchmarkInstance(Benchmark* benchmark, int family_idx,
int per_family_instance_idx,
const std::vector<int64_t>& args, int thread_count);
const BenchmarkName& name() const { return name_; }
int family_index() const { return family_index_; }
int per_family_instance_index() const { return per_family_instance_index_; }
AggregationReportMode aggregation_report_mode() const {
return aggregation_report_mode_;
}
TimeUnit time_unit() const { return time_unit_; }
bool measure_process_cpu_time() const { return measure_process_cpu_time_; }
bool use_real_time() const { return use_real_time_; }
bool use_manual_time() const { return use_manual_time_; }
BigO complexity() const { return complexity_; }
BigOFunc* complexity_lambda() const { return complexity_lambda_; }
const std::vector<Statistics>& statistics() const { return statistics_; }
int repetitions() const { return repetitions_; }
double min_time() const { return min_time_; }
double min_warmup_time() const { return min_warmup_time_; }
IterationCount iterations() const { return iterations_; }
int threads() const { return threads_; }
void Setup() const;
void Teardown() const;
const auto& GetUserThreadRunnerFactory() const {
return benchmark_.threadrunner_;
}
State Run(IterationCount iters, int thread_id, internal::ThreadTimer* timer,
internal::ThreadManager* manager,
internal::PerfCountersMeasurement* perf_counters_measurement,
ProfilerManager* profiler_manager) const;
private:
BenchmarkName name_;
Benchmark& benchmark_;
const int family_index_;
const int per_family_instance_index_;
AggregationReportMode aggregation_report_mode_;
const std::vector<int64_t>& args_;
TimeUnit time_unit_;
bool measure_process_cpu_time_;
bool use_real_time_;
bool use_manual_time_;
BigO complexity_;
BigOFunc* complexity_lambda_;
UserCounters counters_;
const std::vector<Statistics>& statistics_;
int repetitions_;
double min_time_;
double min_warmup_time_;
IterationCount iterations_;
int threads_; // Number of concurrent threads to us
callback_function setup_;
callback_function teardown_;
};
bool FindBenchmarksInternal(const std::string& re,
std::vector<BenchmarkInstance>* benchmarks,
std::ostream* Err);
bool IsZero(double n);
BENCHMARK_EXPORT
ConsoleReporter::OutputOptions GetOutputOptions(bool force_no_color = false);
} // end namespace internal
} // end namespace benchmark
#endif // BENCHMARK_API_INTERNAL_H

View File

@@ -0,0 +1,59 @@
// Copyright 2015 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <benchmark/benchmark.h>
namespace benchmark {
namespace {
// Compute the total size of a pack of std::strings
size_t size_impl() { return 0; }
template <typename Head, typename... Tail>
size_t size_impl(const Head& head, const Tail&... tail) {
return head.size() + size_impl(tail...);
}
// Join a pack of std::strings using a delimiter
// TODO(dominic): use absl::StrJoin
void join_impl(std::string& /*unused*/, char /*unused*/) {}
template <typename Head, typename... Tail>
void join_impl(std::string& s, const char delimiter, const Head& head,
const Tail&... tail) {
if (!s.empty() && !head.empty()) {
s += delimiter;
}
s += head;
join_impl(s, delimiter, tail...);
}
template <typename... Ts>
std::string join(char delimiter, const Ts&... ts) {
std::string s;
s.reserve(sizeof...(Ts) + size_impl(ts...));
join_impl(s, delimiter, ts...);
return s;
}
} // namespace
BENCHMARK_EXPORT
std::string BenchmarkName::str() const {
return join('/', function_name, args, min_time, min_warmup_time, iterations,
repetitions, time_type, threads);
}
} // namespace benchmark

View File

@@ -0,0 +1,544 @@
// Copyright 2015 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "benchmark_register.h"
#ifndef BENCHMARK_OS_WINDOWS
#if !defined(BENCHMARK_OS_FUCHSIA) && !defined(BENCHMARK_OS_QURT)
#include <sys/resource.h>
#endif
#include <sys/time.h>
#include <unistd.h>
#endif
#include <algorithm>
#include <atomic>
#include <cinttypes>
#include <condition_variable>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <iostream>
#include <memory>
#include <numeric>
#include <sstream>
#include <thread>
#include "benchmark/benchmark.h"
#include "benchmark_api_internal.h"
#include "check.h"
#include "commandlineflags.h"
#include "complexity.h"
#include "internal_macros.h"
#include "log.h"
#include "mutex.h"
#include "re.h"
#include "statistics.h"
#include "string_util.h"
#include "timers.h"
namespace benchmark {
namespace {
// For non-dense Range, intermediate values are powers of kRangeMultiplier.
constexpr int kRangeMultiplier = 8;
// The size of a benchmark family determines is the number of inputs to repeat
// the benchmark on. If this is "large" then warn the user during configuration.
constexpr size_t kMaxFamilySize = 100;
constexpr char kDisabledPrefix[] = "DISABLED_";
} // end namespace
namespace internal {
//=============================================================================//
// BenchmarkFamilies
//=============================================================================//
// Class for managing registered benchmarks. Note that each registered
// benchmark identifies a family of related benchmarks to run.
class BenchmarkFamilies {
public:
static BenchmarkFamilies* GetInstance();
// Registers a benchmark family and returns the index assigned to it.
size_t AddBenchmark(std::unique_ptr<Benchmark> family);
// Clear all registered benchmark families.
void ClearBenchmarks();
// Extract the list of benchmark instances that match the specified
// regular expression.
bool FindBenchmarks(std::string spec,
std::vector<BenchmarkInstance>* benchmarks,
std::ostream* Err);
private:
BenchmarkFamilies() {}
std::vector<std::unique_ptr<Benchmark>> families_;
Mutex mutex_;
};
BenchmarkFamilies* BenchmarkFamilies::GetInstance() {
static BenchmarkFamilies instance;
return &instance;
}
size_t BenchmarkFamilies::AddBenchmark(std::unique_ptr<Benchmark> family) {
MutexLock l(mutex_);
size_t index = families_.size();
families_.push_back(std::move(family));
return index;
}
void BenchmarkFamilies::ClearBenchmarks() {
MutexLock l(mutex_);
families_.clear();
families_.shrink_to_fit();
}
bool BenchmarkFamilies::FindBenchmarks(
std::string spec, std::vector<BenchmarkInstance>* benchmarks,
std::ostream* ErrStream) {
BM_CHECK(ErrStream);
auto& Err = *ErrStream;
// Make regular expression out of command-line flag
std::string error_msg;
Regex re;
bool is_negative_filter = false;
if (spec[0] == '-') {
spec.replace(0, 1, "");
is_negative_filter = true;
}
if (!re.Init(spec, &error_msg)) {
Err << "Could not compile benchmark re: " << error_msg << '\n';
return false;
}
// Special list of thread counts to use when none are specified
const std::vector<int> one_thread = {1};
int next_family_index = 0;
MutexLock l(mutex_);
for (std::unique_ptr<Benchmark>& family : families_) {
int family_index = next_family_index;
int per_family_instance_index = 0;
// Family was deleted or benchmark doesn't match
if (!family) {
continue;
}
if (family->ArgsCnt() == -1) {
family->Args({});
}
const std::vector<int>* thread_counts =
(family->thread_counts_.empty()
? &one_thread
: &static_cast<const std::vector<int>&>(family->thread_counts_));
const size_t family_size = family->args_.size() * thread_counts->size();
// The benchmark will be run at least 'family_size' different inputs.
// If 'family_size' is very large warn the user.
if (family_size > kMaxFamilySize) {
Err << "The number of inputs is very large. " << family->name_
<< " will be repeated at least " << family_size << " times.\n";
}
// reserve in the special case the regex ".", since we know the final
// family size. this doesn't take into account any disabled benchmarks
// so worst case we reserve more than we need.
if (spec == ".") {
benchmarks->reserve(benchmarks->size() + family_size);
}
for (auto const& args : family->args_) {
for (int num_threads : *thread_counts) {
BenchmarkInstance instance(family.get(), family_index,
per_family_instance_index, args,
num_threads);
const auto full_name = instance.name().str();
if (full_name.rfind(kDisabledPrefix, 0) != 0 &&
((re.Match(full_name) && !is_negative_filter) ||
(!re.Match(full_name) && is_negative_filter))) {
benchmarks->push_back(std::move(instance));
++per_family_instance_index;
// Only bump the next family index once we've estabilished that
// at least one instance of this family will be run.
if (next_family_index == family_index) {
++next_family_index;
}
}
}
}
}
return true;
}
Benchmark* RegisterBenchmarkInternal(std::unique_ptr<Benchmark> bench) {
Benchmark* bench_ptr = bench.get();
BenchmarkFamilies* families = BenchmarkFamilies::GetInstance();
families->AddBenchmark(std::move(bench));
return bench_ptr;
}
// FIXME: This function is a hack so that benchmark.cc can access
// `BenchmarkFamilies`
bool FindBenchmarksInternal(const std::string& re,
std::vector<BenchmarkInstance>* benchmarks,
std::ostream* Err) {
return BenchmarkFamilies::GetInstance()->FindBenchmarks(re, benchmarks, Err);
}
//=============================================================================//
// Benchmark
//=============================================================================//
Benchmark::Benchmark(const std::string& name)
: name_(name),
aggregation_report_mode_(ARM_Unspecified),
time_unit_(GetDefaultTimeUnit()),
use_default_time_unit_(true),
range_multiplier_(kRangeMultiplier),
min_time_(0),
min_warmup_time_(0),
iterations_(0),
repetitions_(0),
measure_process_cpu_time_(false),
use_real_time_(false),
use_manual_time_(false),
complexity_(oNone),
complexity_lambda_(nullptr) {
ComputeStatistics("mean", StatisticsMean);
ComputeStatistics("median", StatisticsMedian);
ComputeStatistics("stddev", StatisticsStdDev);
ComputeStatistics("cv", StatisticsCV, kPercentage);
}
Benchmark::~Benchmark() {}
Benchmark* Benchmark::Name(const std::string& name) {
SetName(name);
return this;
}
Benchmark* Benchmark::Arg(int64_t x) {
BM_CHECK(ArgsCnt() == -1 || ArgsCnt() == 1);
args_.push_back({x});
return this;
}
Benchmark* Benchmark::Unit(TimeUnit unit) {
time_unit_ = unit;
use_default_time_unit_ = false;
return this;
}
Benchmark* Benchmark::Range(int64_t start, int64_t limit) {
BM_CHECK(ArgsCnt() == -1 || ArgsCnt() == 1);
std::vector<int64_t> arglist;
AddRange(&arglist, start, limit, range_multiplier_);
for (int64_t i : arglist) {
args_.push_back({i});
}
return this;
}
Benchmark* Benchmark::Ranges(
const std::vector<std::pair<int64_t, int64_t>>& ranges) {
BM_CHECK(ArgsCnt() == -1 || ArgsCnt() == static_cast<int>(ranges.size()));
std::vector<std::vector<int64_t>> arglists(ranges.size());
for (std::size_t i = 0; i < ranges.size(); i++) {
AddRange(&arglists[i], ranges[i].first, ranges[i].second,
range_multiplier_);
}
ArgsProduct(arglists);
return this;
}
Benchmark* Benchmark::ArgsProduct(
const std::vector<std::vector<int64_t>>& arglists) {
BM_CHECK(ArgsCnt() == -1 || ArgsCnt() == static_cast<int>(arglists.size()));
std::vector<std::size_t> indices(arglists.size());
const std::size_t total = std::accumulate(
std::begin(arglists), std::end(arglists), std::size_t{1},
[](const std::size_t res, const std::vector<int64_t>& arglist) {
return res * arglist.size();
});
std::vector<int64_t> args;
args.reserve(arglists.size());
for (std::size_t i = 0; i < total; i++) {
for (std::size_t arg = 0; arg < arglists.size(); arg++) {
args.push_back(arglists[arg][indices[arg]]);
}
args_.push_back(args);
args.clear();
std::size_t arg = 0;
do {
indices[arg] = (indices[arg] + 1) % arglists[arg].size();
} while (indices[arg++] == 0 && arg < arglists.size());
}
return this;
}
Benchmark* Benchmark::ArgName(const std::string& name) {
BM_CHECK(ArgsCnt() == -1 || ArgsCnt() == 1);
arg_names_ = {name};
return this;
}
Benchmark* Benchmark::ArgNames(const std::vector<std::string>& names) {
BM_CHECK(ArgsCnt() == -1 || ArgsCnt() == static_cast<int>(names.size()));
arg_names_ = names;
return this;
}
Benchmark* Benchmark::DenseRange(int64_t start, int64_t limit, int step) {
BM_CHECK(ArgsCnt() == -1 || ArgsCnt() == 1);
BM_CHECK_LE(start, limit);
for (int64_t arg = start; arg <= limit; arg += step) {
args_.push_back({arg});
}
return this;
}
Benchmark* Benchmark::Args(const std::vector<int64_t>& args) {
BM_CHECK(ArgsCnt() == -1 || ArgsCnt() == static_cast<int>(args.size()));
args_.push_back(args);
return this;
}
Benchmark* Benchmark::Apply(void (*custom_arguments)(Benchmark* benchmark)) {
custom_arguments(this);
return this;
}
Benchmark* Benchmark::Setup(callback_function&& setup) {
BM_CHECK(setup != nullptr);
setup_ = std::forward<callback_function>(setup);
return this;
}
Benchmark* Benchmark::Setup(const callback_function& setup) {
BM_CHECK(setup != nullptr);
setup_ = setup;
return this;
}
Benchmark* Benchmark::Teardown(callback_function&& teardown) {
BM_CHECK(teardown != nullptr);
teardown_ = std::forward<callback_function>(teardown);
return this;
}
Benchmark* Benchmark::Teardown(const callback_function& teardown) {
BM_CHECK(teardown != nullptr);
teardown_ = teardown;
return this;
}
Benchmark* Benchmark::RangeMultiplier(int multiplier) {
BM_CHECK(multiplier > 1);
range_multiplier_ = multiplier;
return this;
}
Benchmark* Benchmark::MinTime(double t) {
BM_CHECK(t > 0.0);
BM_CHECK(iterations_ == 0);
min_time_ = t;
return this;
}
Benchmark* Benchmark::MinWarmUpTime(double t) {
BM_CHECK(t >= 0.0);
BM_CHECK(iterations_ == 0);
min_warmup_time_ = t;
return this;
}
Benchmark* Benchmark::Iterations(IterationCount n) {
BM_CHECK(n > 0);
BM_CHECK(IsZero(min_time_));
BM_CHECK(IsZero(min_warmup_time_));
iterations_ = n;
return this;
}
Benchmark* Benchmark::Repetitions(int n) {
BM_CHECK(n > 0);
repetitions_ = n;
return this;
}
Benchmark* Benchmark::ReportAggregatesOnly(bool value) {
aggregation_report_mode_ = value ? ARM_ReportAggregatesOnly : ARM_Default;
return this;
}
Benchmark* Benchmark::DisplayAggregatesOnly(bool value) {
// If we were called, the report mode is no longer 'unspecified', in any case.
aggregation_report_mode_ = static_cast<AggregationReportMode>(
aggregation_report_mode_ | ARM_Default);
if (value) {
aggregation_report_mode_ = static_cast<AggregationReportMode>(
aggregation_report_mode_ | ARM_DisplayReportAggregatesOnly);
} else {
aggregation_report_mode_ = static_cast<AggregationReportMode>(
aggregation_report_mode_ & ~ARM_DisplayReportAggregatesOnly);
}
return this;
}
Benchmark* Benchmark::MeasureProcessCPUTime() {
// Can be used together with UseRealTime() / UseManualTime().
measure_process_cpu_time_ = true;
return this;
}
Benchmark* Benchmark::UseRealTime() {
BM_CHECK(!use_manual_time_)
<< "Cannot set UseRealTime and UseManualTime simultaneously.";
use_real_time_ = true;
return this;
}
Benchmark* Benchmark::UseManualTime() {
BM_CHECK(!use_real_time_)
<< "Cannot set UseRealTime and UseManualTime simultaneously.";
use_manual_time_ = true;
return this;
}
Benchmark* Benchmark::Complexity(BigO complexity) {
complexity_ = complexity;
return this;
}
Benchmark* Benchmark::Complexity(BigOFunc* complexity) {
complexity_lambda_ = complexity;
complexity_ = oLambda;
return this;
}
Benchmark* Benchmark::ComputeStatistics(const std::string& name,
StatisticsFunc* statistics,
StatisticUnit unit) {
statistics_.emplace_back(name, statistics, unit);
return this;
}
Benchmark* Benchmark::Threads(int t) {
BM_CHECK_GT(t, 0);
thread_counts_.push_back(t);
return this;
}
Benchmark* Benchmark::ThreadRange(int min_threads, int max_threads) {
BM_CHECK_GT(min_threads, 0);
BM_CHECK_GE(max_threads, min_threads);
AddRange(&thread_counts_, min_threads, max_threads, 2);
return this;
}
Benchmark* Benchmark::DenseThreadRange(int min_threads, int max_threads,
int stride) {
BM_CHECK_GT(min_threads, 0);
BM_CHECK_GE(max_threads, min_threads);
BM_CHECK_GE(stride, 1);
for (auto i = min_threads; i < max_threads; i += stride) {
thread_counts_.push_back(i);
}
thread_counts_.push_back(max_threads);
return this;
}
Benchmark* Benchmark::ThreadPerCpu() {
thread_counts_.push_back(CPUInfo::Get().num_cpus);
return this;
}
Benchmark* Benchmark::ThreadRunner(threadrunner_factory&& factory) {
threadrunner_ = std::move(factory);
return this;
}
void Benchmark::SetName(const std::string& name) { name_ = name; }
const char* Benchmark::GetName() const { return name_.c_str(); }
int Benchmark::ArgsCnt() const {
if (args_.empty()) {
if (arg_names_.empty()) {
return -1;
}
return static_cast<int>(arg_names_.size());
}
return static_cast<int>(args_.front().size());
}
const char* Benchmark::GetArgName(int arg) const {
BM_CHECK_GE(arg, 0);
size_t uarg = static_cast<size_t>(arg);
BM_CHECK_LT(uarg, arg_names_.size());
return arg_names_[uarg].c_str();
}
TimeUnit Benchmark::GetTimeUnit() const {
return use_default_time_unit_ ? GetDefaultTimeUnit() : time_unit_;
}
//=============================================================================//
// FunctionBenchmark
//=============================================================================//
void FunctionBenchmark::Run(State& st) { func_(st); }
} // end namespace internal
void ClearRegisteredBenchmarks() {
internal::BenchmarkFamilies::GetInstance()->ClearBenchmarks();
}
std::vector<int64_t> CreateRange(int64_t lo, int64_t hi, int multi) {
std::vector<int64_t> args;
internal::AddRange(&args, lo, hi, multi);
return args;
}
std::vector<int64_t> CreateDenseRange(int64_t start, int64_t limit, int step) {
BM_CHECK_LE(start, limit);
std::vector<int64_t> args;
for (int64_t arg = start; arg <= limit; arg += step) {
args.push_back(arg);
}
return args;
}
} // end namespace benchmark

View File

@@ -0,0 +1,109 @@
#ifndef BENCHMARK_REGISTER_H
#define BENCHMARK_REGISTER_H
#include <algorithm>
#include <limits>
#include <vector>
#include "check.h"
namespace benchmark {
namespace internal {
// Append the powers of 'mult' in the closed interval [lo, hi].
// Returns iterator to the start of the inserted range.
template <typename T>
typename std::vector<T>::iterator AddPowers(std::vector<T>* dst, T lo, T hi,
int mult) {
BM_CHECK_GE(lo, 0);
BM_CHECK_GE(hi, lo);
BM_CHECK_GE(mult, 2);
const size_t start_offset = dst->size();
static const T kmax = std::numeric_limits<T>::max();
// Space out the values in multiples of "mult"
for (T i = static_cast<T>(1); i <= hi; i = static_cast<T>(i * mult)) {
if (i >= lo) {
dst->push_back(i);
}
// Break the loop here since multiplying by
// 'mult' would move outside of the range of T
if (i > kmax / mult) break;
}
return dst->begin() + static_cast<int>(start_offset);
}
template <typename T>
void AddNegatedPowers(std::vector<T>* dst, T lo, T hi, int mult) {
// We negate lo and hi so we require that they cannot be equal to 'min'.
BM_CHECK_GT(lo, std::numeric_limits<T>::min());
BM_CHECK_GT(hi, std::numeric_limits<T>::min());
BM_CHECK_GE(hi, lo);
BM_CHECK_LE(hi, 0);
// Add positive powers, then negate and reverse.
// Casts necessary since small integers get promoted
// to 'int' when negating.
const auto lo_complement = static_cast<T>(-lo);
const auto hi_complement = static_cast<T>(-hi);
const auto it = AddPowers(dst, hi_complement, lo_complement, mult);
std::for_each(it, dst->end(), [](T& t) { t = static_cast<T>(t * -1); });
std::reverse(it, dst->end());
}
template <typename T>
void AddRange(std::vector<T>* dst, T lo, T hi, int mult) {
static_assert(std::is_integral<T>::value && std::is_signed<T>::value,
"Args type must be a signed integer");
BM_CHECK_GE(hi, lo);
BM_CHECK_GE(mult, 2);
// Add "lo"
dst->push_back(lo);
// Handle lo == hi as a special case, so we then know
// lo < hi and so it is safe to add 1 to lo and subtract 1
// from hi without falling outside of the range of T.
if (lo == hi) return;
// Ensure that lo_inner <= hi_inner below.
if (lo + 1 == hi) {
dst->push_back(hi);
return;
}
// Add all powers of 'mult' in the range [lo+1, hi-1] (inclusive).
const auto lo_inner = static_cast<T>(lo + 1);
const auto hi_inner = static_cast<T>(hi - 1);
// Insert negative values
if (lo_inner < 0) {
AddNegatedPowers(dst, lo_inner, std::min(hi_inner, T{-1}), mult);
}
// Treat 0 as a special case (see discussion on #762).
if (lo < 0 && hi >= 0) {
dst->push_back(0);
}
// Insert positive values
if (hi_inner > 0) {
AddPowers(dst, std::max(lo_inner, T{1}), hi_inner, mult);
}
// Add "hi" (if different from last value).
if (hi != dst->back()) {
dst->push_back(hi);
}
}
} // namespace internal
} // namespace benchmark
#endif // BENCHMARK_REGISTER_H

View File

@@ -0,0 +1,573 @@
// Copyright 2015 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "benchmark_runner.h"
#include "benchmark/benchmark.h"
#include "benchmark_api_internal.h"
#include "internal_macros.h"
#ifndef BENCHMARK_OS_WINDOWS
#if !defined(BENCHMARK_OS_FUCHSIA) && !defined(BENCHMARK_OS_QURT)
#include <sys/resource.h>
#endif
#include <sys/time.h>
#include <unistd.h>
#endif
#include <algorithm>
#include <atomic>
#include <climits>
#include <cmath>
#include <condition_variable>
#include <cstdio>
#include <cstdlib>
#include <fstream>
#include <functional>
#include <iostream>
#include <limits>
#include <memory>
#include <string>
#include <thread>
#include <utility>
#include "check.h"
#include "colorprint.h"
#include "commandlineflags.h"
#include "complexity.h"
#include "counter.h"
#include "log.h"
#include "mutex.h"
#include "perf_counters.h"
#include "re.h"
#include "statistics.h"
#include "string_util.h"
#include "thread_manager.h"
#include "thread_timer.h"
namespace benchmark {
BM_DECLARE_bool(benchmark_dry_run);
BM_DECLARE_string(benchmark_min_time);
BM_DECLARE_double(benchmark_min_warmup_time);
BM_DECLARE_int32(benchmark_repetitions);
BM_DECLARE_bool(benchmark_report_aggregates_only);
BM_DECLARE_bool(benchmark_display_aggregates_only);
BM_DECLARE_string(benchmark_perf_counters);
namespace internal {
MemoryManager* memory_manager = nullptr;
ProfilerManager* profiler_manager = nullptr;
namespace {
constexpr IterationCount kMaxIterations = 1000000000000;
const double kDefaultMinTime =
std::strtod(::benchmark::kDefaultMinTimeStr, /*p_end*/ nullptr);
BenchmarkReporter::Run CreateRunReport(
const benchmark::internal::BenchmarkInstance& b,
const internal::ThreadManager::Result& results,
IterationCount memory_iterations,
const MemoryManager::Result& memory_result, double seconds,
int64_t repetition_index, int64_t repeats) {
// Create report about this benchmark run.
BenchmarkReporter::Run report;
report.run_name = b.name();
report.family_index = b.family_index();
report.per_family_instance_index = b.per_family_instance_index();
report.skipped = results.skipped_;
report.skip_message = results.skip_message_;
report.report_label = results.report_label_;
// This is the total iterations across all threads.
report.iterations = results.iterations;
report.time_unit = b.time_unit();
report.threads = b.threads();
report.repetition_index = repetition_index;
report.repetitions = repeats;
if (report.skipped == 0u) {
if (b.use_manual_time()) {
report.real_accumulated_time = results.manual_time_used;
} else {
report.real_accumulated_time = results.real_time_used;
}
report.use_real_time_for_initial_big_o = b.use_manual_time();
report.cpu_accumulated_time = results.cpu_time_used;
report.complexity_n = results.complexity_n;
report.complexity = b.complexity();
report.complexity_lambda = b.complexity_lambda();
report.statistics = &b.statistics();
report.counters = results.counters;
if (memory_iterations > 0) {
report.memory_result = memory_result;
report.allocs_per_iter =
memory_iterations != 0
? static_cast<double>(memory_result.num_allocs) /
static_cast<double>(memory_iterations)
: 0;
}
internal::Finish(&report.counters, results.iterations, seconds,
b.threads());
}
return report;
}
// Execute one thread of benchmark b for the specified number of iterations.
// Adds the stats collected for the thread into manager->results.
void RunInThread(const BenchmarkInstance* b, IterationCount iters,
int thread_id, ThreadManager* manager,
PerfCountersMeasurement* perf_counters_measurement,
ProfilerManager* profiler_manager_) {
internal::ThreadTimer timer(
b->measure_process_cpu_time()
? internal::ThreadTimer::CreateProcessCpuTime()
: internal::ThreadTimer::Create());
State st = b->Run(iters, thread_id, &timer, manager,
perf_counters_measurement, profiler_manager_);
if (!(st.skipped() || st.iterations() >= st.max_iterations)) {
st.SkipWithError(
"The benchmark didn't run, nor was it explicitly skipped. Please call "
"'SkipWithXXX` in your benchmark as appropriate.");
}
{
MutexLock l(manager->GetBenchmarkMutex());
internal::ThreadManager::Result& results = manager->results;
results.iterations += st.iterations();
results.cpu_time_used += timer.cpu_time_used();
results.real_time_used += timer.real_time_used();
results.manual_time_used += timer.manual_time_used();
results.complexity_n += st.complexity_length_n();
internal::Increment(&results.counters, st.counters);
}
manager->NotifyThreadComplete();
}
double ComputeMinTime(const benchmark::internal::BenchmarkInstance& b,
const BenchTimeType& iters_or_time) {
if (!IsZero(b.min_time())) {
return b.min_time();
}
// If the flag was used to specify number of iters, then return the default
// min_time.
if (iters_or_time.tag == BenchTimeType::ITERS) {
return kDefaultMinTime;
}
return iters_or_time.time;
}
IterationCount ComputeIters(const benchmark::internal::BenchmarkInstance& b,
const BenchTimeType& iters_or_time) {
if (b.iterations() != 0) {
return b.iterations();
}
// We've already concluded that this flag is currently used to pass
// iters but do a check here again anyway.
BM_CHECK(iters_or_time.tag == BenchTimeType::ITERS);
return iters_or_time.iters;
}
class ThreadRunnerDefault : public ThreadRunnerBase {
public:
explicit ThreadRunnerDefault(int num_threads)
: pool(static_cast<size_t>(num_threads - 1)) {}
void RunThreads(const std::function<void(int)>& fn) final {
// Run all but one thread in separate threads
for (std::size_t ti = 0; ti < pool.size(); ++ti) {
pool[ti] = std::thread(fn, static_cast<int>(ti + 1));
}
// And run one thread here directly.
// (If we were asked to run just one thread, we don't create new threads.)
// Yes, we need to do this here *after* we start the separate threads.
fn(0);
// The main thread has finished. Now let's wait for the other threads.
for (std::thread& thread : pool) {
thread.join();
}
}
private:
std::vector<std::thread> pool;
};
std::unique_ptr<ThreadRunnerBase> GetThreadRunner(
const threadrunner_factory& userThreadRunnerFactory, int num_threads) {
return userThreadRunnerFactory
? userThreadRunnerFactory(num_threads)
: std::make_unique<ThreadRunnerDefault>(num_threads);
}
} // end namespace
BenchTimeType ParseBenchMinTime(const std::string& value) {
BenchTimeType ret = {};
if (value.empty()) {
ret.tag = BenchTimeType::TIME;
ret.time = 0.0;
return ret;
}
if (value.back() == 'x') {
char* p_end = nullptr;
// Reset errno before it's changed by strtol.
errno = 0;
IterationCount num_iters = std::strtol(value.c_str(), &p_end, 10);
// After a valid parse, p_end should have been set to
// point to the 'x' suffix.
BM_CHECK(errno == 0 && p_end != nullptr && *p_end == 'x')
<< "Malformed iters value passed to --benchmark_min_time: `" << value
<< "`. Expected --benchmark_min_time=<integer>x.";
ret.tag = BenchTimeType::ITERS;
ret.iters = num_iters;
return ret;
}
bool has_suffix = value.back() == 's';
if (!has_suffix) {
BM_VLOG(0) << "Value passed to --benchmark_min_time should have a suffix. "
"Eg., `30s` for 30-seconds.";
}
char* p_end = nullptr;
// Reset errno before it's changed by strtod.
errno = 0;
double min_time = std::strtod(value.c_str(), &p_end);
// After a successful parse, p_end should point to the suffix 's',
// or the end of the string if the suffix was omitted.
BM_CHECK(errno == 0 && p_end != nullptr &&
((has_suffix && *p_end == 's') || *p_end == '\0'))
<< "Malformed seconds value passed to --benchmark_min_time: `" << value
<< "`. Expected --benchmark_min_time=<float>x.";
ret.tag = BenchTimeType::TIME;
ret.time = min_time;
return ret;
}
BenchmarkRunner::BenchmarkRunner(
const benchmark::internal::BenchmarkInstance& b_,
PerfCountersMeasurement* pcm_,
BenchmarkReporter::PerFamilyRunReports* reports_for_family_)
: b(b_),
reports_for_family(reports_for_family_),
parsed_benchtime_flag(ParseBenchMinTime(FLAGS_benchmark_min_time)),
min_time(FLAGS_benchmark_dry_run
? 0
: ComputeMinTime(b_, parsed_benchtime_flag)),
min_warmup_time(
FLAGS_benchmark_dry_run
? 0
: ((!IsZero(b.min_time()) && b.min_warmup_time() > 0.0)
? b.min_warmup_time()
: FLAGS_benchmark_min_warmup_time)),
warmup_done(FLAGS_benchmark_dry_run ? true : !(min_warmup_time > 0.0)),
repeats(FLAGS_benchmark_dry_run
? 1
: (b.repetitions() != 0 ? b.repetitions()
: FLAGS_benchmark_repetitions)),
has_explicit_iteration_count(b.iterations() != 0 ||
parsed_benchtime_flag.tag ==
BenchTimeType::ITERS),
thread_runner(
GetThreadRunner(b.GetUserThreadRunnerFactory(), b.threads())),
iters(FLAGS_benchmark_dry_run
? 1
: (has_explicit_iteration_count
? ComputeIters(b_, parsed_benchtime_flag)
: 1)),
perf_counters_measurement_ptr(pcm_) {
run_results.display_report_aggregates_only =
(FLAGS_benchmark_report_aggregates_only ||
FLAGS_benchmark_display_aggregates_only);
run_results.file_report_aggregates_only =
FLAGS_benchmark_report_aggregates_only;
if (b.aggregation_report_mode() != internal::ARM_Unspecified) {
run_results.display_report_aggregates_only =
((b.aggregation_report_mode() &
internal::ARM_DisplayReportAggregatesOnly) != 0u);
run_results.file_report_aggregates_only =
((b.aggregation_report_mode() &
internal::ARM_FileReportAggregatesOnly) != 0u);
BM_CHECK(FLAGS_benchmark_perf_counters.empty() ||
(perf_counters_measurement_ptr->num_counters() == 0))
<< "Perf counters were requested but could not be set up.";
}
}
BenchmarkRunner::IterationResults BenchmarkRunner::DoNIterations() {
BM_VLOG(2) << "Running " << b.name().str() << " for " << iters << "\n";
std::unique_ptr<internal::ThreadManager> manager;
manager.reset(new internal::ThreadManager(b.threads()));
thread_runner->RunThreads([&](int thread_idx) {
RunInThread(&b, iters, thread_idx, manager.get(),
perf_counters_measurement_ptr, /*profiler_manager=*/nullptr);
});
IterationResults i;
// Acquire the measurements/counters from the manager, UNDER THE LOCK!
{
MutexLock l(manager->GetBenchmarkMutex());
i.results = manager->results;
}
// And get rid of the manager.
manager.reset();
BM_VLOG(2) << "Ran in " << i.results.cpu_time_used << "/"
<< i.results.real_time_used << "\n";
// By using KeepRunningBatch a benchmark can iterate more times than
// requested, so take the iteration count from i.results.
i.iters = i.results.iterations / b.threads();
// Base decisions off of real time if requested by this benchmark.
i.seconds = i.results.cpu_time_used;
if (b.use_manual_time()) {
i.seconds = i.results.manual_time_used;
} else if (b.use_real_time()) {
i.seconds = i.results.real_time_used;
}
return i;
}
IterationCount BenchmarkRunner::PredictNumItersNeeded(
const IterationResults& i) const {
// See how much iterations should be increased by.
// Note: Avoid division by zero with max(seconds, 1ns).
double multiplier = GetMinTimeToApply() * 1.4 / std::max(i.seconds, 1e-9);
// If our last run was at least 10% of FLAGS_benchmark_min_time then we
// use the multiplier directly.
// Otherwise we use at most 10 times expansion.
// NOTE: When the last run was at least 10% of the min time the max
// expansion should be 14x.
const bool is_significant = (i.seconds / GetMinTimeToApply()) > 0.1;
multiplier = is_significant ? multiplier : 10.0;
// So what seems to be the sufficiently-large iteration count? Round up.
const IterationCount max_next_iters = static_cast<IterationCount>(
std::llround(std::max(multiplier * static_cast<double>(i.iters),
static_cast<double>(i.iters) + 1.0)));
// But we do have *some* limits though..
const IterationCount next_iters = std::min(max_next_iters, kMaxIterations);
BM_VLOG(3) << "Next iters: " << next_iters << ", " << multiplier << "\n";
return next_iters; // round up before conversion to integer.
}
bool BenchmarkRunner::ShouldReportIterationResults(
const IterationResults& i) const {
// Determine if this run should be reported;
// Either it has run for a sufficient amount of time
// or because an error was reported.
return (i.results.skipped_ != 0u) || FLAGS_benchmark_dry_run ||
i.iters >= kMaxIterations || // Too many iterations already.
i.seconds >=
GetMinTimeToApply() || // The elapsed time is large enough.
// CPU time is specified but the elapsed real time greatly exceeds
// the minimum time.
// Note that user provided timers are except from this test.
((i.results.real_time_used >= 5 * GetMinTimeToApply()) &&
!b.use_manual_time());
}
double BenchmarkRunner::GetMinTimeToApply() const {
// In order to re-use functionality to run and measure benchmarks for running
// a warmup phase of the benchmark, we need a way of telling whether to apply
// min_time or min_warmup_time. This function will figure out if we are in the
// warmup phase and therefore need to apply min_warmup_time or if we already
// in the benchmarking phase and min_time needs to be applied.
return warmup_done ? min_time : min_warmup_time;
}
void BenchmarkRunner::FinishWarmUp(const IterationCount& i) {
warmup_done = true;
iters = i;
}
void BenchmarkRunner::RunWarmUp() {
// Use the same mechanisms for warming up the benchmark as used for actually
// running and measuring the benchmark.
IterationResults i_warmup;
// Dont use the iterations determined in the warmup phase for the actual
// measured benchmark phase. While this may be a good starting point for the
// benchmark and it would therefore get rid of the need to figure out how many
// iterations are needed if min_time is set again, this may also be a complete
// wrong guess since the warmup loops might be considerably slower (e.g
// because of caching effects).
const IterationCount i_backup = iters;
for (;;) {
b.Setup();
i_warmup = DoNIterations();
b.Teardown();
const bool finish = ShouldReportIterationResults(i_warmup);
if (finish) {
FinishWarmUp(i_backup);
break;
}
// Although we are running "only" a warmup phase where running enough
// iterations at once without measuring time isn't as important as it is for
// the benchmarking phase, we still do it the same way as otherwise it is
// very confusing for the user to know how to choose a proper value for
// min_warmup_time if a different approach on running it is used.
iters = PredictNumItersNeeded(i_warmup);
assert(iters > i_warmup.iters &&
"if we did more iterations than we want to do the next time, "
"then we should have accepted the current iteration run.");
}
}
MemoryManager::Result BenchmarkRunner::RunMemoryManager(
IterationCount memory_iterations) {
memory_manager->Start();
std::unique_ptr<internal::ThreadManager> manager;
manager.reset(new internal::ThreadManager(1));
b.Setup();
RunInThread(&b, memory_iterations, 0, manager.get(),
perf_counters_measurement_ptr,
/*profiler_manager=*/nullptr);
manager.reset();
b.Teardown();
MemoryManager::Result memory_result;
memory_manager->Stop(memory_result);
memory_result.memory_iterations = memory_iterations;
return memory_result;
}
void BenchmarkRunner::RunProfilerManager(IterationCount profile_iterations) {
std::unique_ptr<internal::ThreadManager> manager;
manager.reset(new internal::ThreadManager(1));
b.Setup();
RunInThread(&b, profile_iterations, 0, manager.get(),
/*perf_counters_measurement_ptr=*/nullptr,
/*profiler_manager=*/profiler_manager);
manager.reset();
b.Teardown();
}
void BenchmarkRunner::DoOneRepetition() {
assert(HasRepeatsRemaining() && "Already done all repetitions?");
const bool is_the_first_repetition = num_repetitions_done == 0;
// In case a warmup phase is requested by the benchmark, run it now.
// After running the warmup phase the BenchmarkRunner should be in a state as
// this warmup never happened except the fact that warmup_done is set. Every
// other manipulation of the BenchmarkRunner instance would be a bug! Please
// fix it.
if (!warmup_done) {
RunWarmUp();
}
IterationResults i;
// We *may* be gradually increasing the length (iteration count)
// of the benchmark until we decide the results are significant.
// And once we do, we report those last results and exit.
// Please do note that the if there are repetitions, the iteration count
// is *only* calculated for the *first* repetition, and other repetitions
// simply use that precomputed iteration count.
for (;;) {
b.Setup();
i = DoNIterations();
b.Teardown();
// Do we consider the results to be significant?
// If we are doing repetitions, and the first repetition was already done,
// it has calculated the correct iteration time, so we have run that very
// iteration count just now. No need to calculate anything. Just report.
// Else, the normal rules apply.
const bool results_are_significant = !is_the_first_repetition ||
has_explicit_iteration_count ||
ShouldReportIterationResults(i);
// Good, let's report them!
if (results_are_significant) {
break;
}
// Nope, bad iteration. Let's re-estimate the hopefully-sufficient
// iteration count, and run the benchmark again...
iters = PredictNumItersNeeded(i);
assert(iters > i.iters &&
"if we did more iterations than we want to do the next time, "
"then we should have accepted the current iteration run.");
}
// Produce memory measurements if requested.
MemoryManager::Result memory_result;
IterationCount memory_iterations = 0;
if (memory_manager != nullptr) {
// Only run a few iterations to reduce the impact of one-time
// allocations in benchmarks that are not properly managed.
memory_iterations = std::min<IterationCount>(16, iters);
memory_result = RunMemoryManager(memory_iterations);
}
if (profiler_manager != nullptr) {
// We want to externally profile the benchmark for the same number of
// iterations because, for example, if we're tracing the benchmark then we
// want trace data to reasonably match PMU data.
RunProfilerManager(iters);
}
// Ok, now actually report.
BenchmarkReporter::Run report =
CreateRunReport(b, i.results, memory_iterations, memory_result, i.seconds,
num_repetitions_done, repeats);
if (reports_for_family != nullptr) {
++reports_for_family->num_runs_done;
if (report.skipped == 0u) {
reports_for_family->Runs.push_back(report);
}
}
run_results.non_aggregates.push_back(report);
++num_repetitions_done;
}
RunResults&& BenchmarkRunner::GetResults() {
assert(!HasRepeatsRemaining() && "Did not run all repetitions yet?");
// Calculate additional statistics over the repetitions of this instance.
run_results.aggregates_only = ComputeStats(run_results.non_aggregates);
return std::move(run_results);
}
} // end namespace internal
} // end namespace benchmark

View File

@@ -0,0 +1,127 @@
// Copyright 2015 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef BENCHMARK_RUNNER_H_
#define BENCHMARK_RUNNER_H_
#include <memory>
#include <thread>
#include <vector>
#include "benchmark_api_internal.h"
#include "perf_counters.h"
#include "thread_manager.h"
namespace benchmark {
namespace internal {
extern MemoryManager* memory_manager;
extern ProfilerManager* profiler_manager;
struct RunResults {
std::vector<BenchmarkReporter::Run> non_aggregates;
std::vector<BenchmarkReporter::Run> aggregates_only;
bool display_report_aggregates_only = false;
bool file_report_aggregates_only = false;
};
struct BENCHMARK_EXPORT BenchTimeType {
enum { UNSPECIFIED, ITERS, TIME } tag;
union {
IterationCount iters;
double time;
};
};
BENCHMARK_EXPORT
BenchTimeType ParseBenchMinTime(const std::string& value);
class BenchmarkRunner {
public:
BenchmarkRunner(const benchmark::internal::BenchmarkInstance& b_,
benchmark::internal::PerfCountersMeasurement* pcm_,
BenchmarkReporter::PerFamilyRunReports* reports_for_family);
int GetNumRepeats() const { return repeats; }
bool HasRepeatsRemaining() const {
return GetNumRepeats() != num_repetitions_done;
}
void DoOneRepetition();
RunResults&& GetResults();
BenchmarkReporter::PerFamilyRunReports* GetReportsForFamily() const {
return reports_for_family;
}
double GetMinTime() const { return min_time; }
bool HasExplicitIters() const { return has_explicit_iteration_count; }
IterationCount GetIters() const { return iters; }
private:
RunResults run_results;
const benchmark::internal::BenchmarkInstance& b;
BenchmarkReporter::PerFamilyRunReports* reports_for_family;
BenchTimeType parsed_benchtime_flag;
const double min_time;
const double min_warmup_time;
bool warmup_done;
const int repeats;
const bool has_explicit_iteration_count;
int num_repetitions_done = 0;
std::unique_ptr<ThreadRunnerBase> thread_runner;
IterationCount iters; // preserved between repetitions!
// So only the first repetition has to find/calculate it,
// the other repetitions will just use that precomputed iteration count.
PerfCountersMeasurement* const perf_counters_measurement_ptr = nullptr;
struct IterationResults {
internal::ThreadManager::Result results;
IterationCount iters;
double seconds;
};
IterationResults DoNIterations();
MemoryManager::Result RunMemoryManager(IterationCount memory_iterations);
void RunProfilerManager(IterationCount profile_iterations);
IterationCount PredictNumItersNeeded(const IterationResults& i) const;
bool ShouldReportIterationResults(const IterationResults& i) const;
double GetMinTimeToApply() const;
void FinishWarmUp(const IterationCount& i);
void RunWarmUp();
};
} // namespace internal
} // end namespace benchmark
#endif // BENCHMARK_RUNNER_H_

View File

@@ -0,0 +1,14 @@
#include "check.h"
namespace benchmark {
namespace internal {
namespace {
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
AbortHandlerT* handler = &std::abort;
} // namespace
BENCHMARK_EXPORT AbortHandlerT*& GetAbortHandler() { return handler; }
} // namespace internal
} // namespace benchmark

View File

@@ -0,0 +1,112 @@
#ifndef CHECK_H_
#define CHECK_H_
#include <cmath>
#include <cstdlib>
#include <ostream>
#include <string_view>
#include "benchmark/export.h"
#include "internal_macros.h"
#include "log.h"
#if defined(__GNUC__) || defined(__clang__)
#define BENCHMARK_NOEXCEPT noexcept
#define BENCHMARK_NOEXCEPT_OP(x) noexcept(x)
#elif defined(_MSC_VER) && !defined(__clang__)
#if _MSC_VER >= 1900
#define BENCHMARK_NOEXCEPT noexcept
#define BENCHMARK_NOEXCEPT_OP(x) noexcept(x)
#else
#define BENCHMARK_NOEXCEPT
#define BENCHMARK_NOEXCEPT_OP(x)
#endif
#define __func__ __FUNCTION__
#else
#define BENCHMARK_NOEXCEPT
#define BENCHMARK_NOEXCEPT_OP(x)
#endif
namespace benchmark {
namespace internal {
typedef void(AbortHandlerT)();
BENCHMARK_EXPORT
AbortHandlerT*& GetAbortHandler();
BENCHMARK_NORETURN inline void CallAbortHandler() {
GetAbortHandler()();
std::flush(std::cout);
std::flush(std::cerr);
std::abort(); // fallback to enforce noreturn
}
// CheckHandler is the class constructed by failing BM_CHECK macros.
// CheckHandler will log information about the failures and abort when it is
// destructed.
class CheckHandler {
public:
CheckHandler(std::string_view check, std::string_view file,
std::string_view func, int line)
: log_(GetErrorLogInstance()) {
log_ << file << ":" << line << ": " << func << ": Check `" << check
<< "' failed. ";
}
LogType& GetLog() { return log_; }
#if defined(COMPILER_MSVC)
#pragma warning(push)
#pragma warning(disable : 4722)
#endif
BENCHMARK_NORETURN ~CheckHandler() BENCHMARK_NOEXCEPT_OP(false) {
log_ << '\n';
CallAbortHandler();
}
#if defined(COMPILER_MSVC)
#pragma warning(pop)
#endif
CheckHandler& operator=(const CheckHandler&) = delete;
CheckHandler(const CheckHandler&) = delete;
CheckHandler() = delete;
private:
LogType& log_;
};
} // end namespace internal
} // end namespace benchmark
// The BM_CHECK macro returns a std::ostream object that can have extra
// information written to it.
#ifndef NDEBUG
#define BM_CHECK(b) \
(b ? ::benchmark::internal::GetNullLogInstance() \
: ::benchmark::internal::CheckHandler( \
std::string_view(#b), std::string_view(__FILE__), \
std::string_view(__func__), __LINE__) \
.GetLog())
#else
#define BM_CHECK(b) ::benchmark::internal::GetNullLogInstance()
#endif
// clang-format off
// preserve whitespacing between operators for alignment
#define BM_CHECK_EQ(a, b) BM_CHECK((a) == (b))
#define BM_CHECK_NE(a, b) BM_CHECK((a) != (b))
#define BM_CHECK_GE(a, b) BM_CHECK((a) >= (b))
#define BM_CHECK_LE(a, b) BM_CHECK((a) <= (b))
#define BM_CHECK_GT(a, b) BM_CHECK((a) > (b))
#define BM_CHECK_LT(a, b) BM_CHECK((a) < (b))
#define BM_CHECK_FLOAT_EQ(a, b, eps) BM_CHECK(std::fabs((a) - (b)) < (eps))
#define BM_CHECK_FLOAT_NE(a, b, eps) BM_CHECK(std::fabs((a) - (b)) >= (eps))
#define BM_CHECK_FLOAT_GE(a, b, eps) BM_CHECK((a) - (b) > -(eps))
#define BM_CHECK_FLOAT_LE(a, b, eps) BM_CHECK((b) - (a) > -(eps))
#define BM_CHECK_FLOAT_GT(a, b, eps) BM_CHECK((a) - (b) > (eps))
#define BM_CHECK_FLOAT_LT(a, b, eps) BM_CHECK((b) - (a) > (eps))
//clang-format on
#endif // CHECK_H_

View File

@@ -0,0 +1,222 @@
// Copyright 2015 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "colorprint.h"
#include <cstdarg>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <memory>
#include <string>
#include "check.h"
#include "internal_macros.h"
#ifdef BENCHMARK_OS_WINDOWS
#include <io.h>
#include <windows.h>
#else
#include <unistd.h>
#endif // BENCHMARK_OS_WINDOWS
namespace benchmark {
namespace {
#ifdef BENCHMARK_OS_WINDOWS
typedef WORD PlatformColorCode;
#else
typedef const char* PlatformColorCode;
#endif
PlatformColorCode GetPlatformColorCode(LogColor color) {
#ifdef BENCHMARK_OS_WINDOWS
switch (color) {
case COLOR_RED:
return FOREGROUND_RED;
case COLOR_GREEN:
return FOREGROUND_GREEN;
case COLOR_YELLOW:
return FOREGROUND_RED | FOREGROUND_GREEN;
case COLOR_BLUE:
return FOREGROUND_BLUE;
case COLOR_MAGENTA:
return FOREGROUND_BLUE | FOREGROUND_RED;
case COLOR_CYAN:
return FOREGROUND_BLUE | FOREGROUND_GREEN;
case COLOR_WHITE: // fall through to default
default:
return 0;
}
#else
switch (color) {
case COLOR_RED:
return "1";
case COLOR_GREEN:
return "2";
case COLOR_YELLOW:
return "3";
case COLOR_BLUE:
return "4";
case COLOR_MAGENTA:
return "5";
case COLOR_CYAN:
return "6";
case COLOR_WHITE:
return "7";
default:
return nullptr;
};
#endif
}
} // end namespace
std::string FormatString(const char* msg, va_list args) {
// we might need a second shot at this, so pre-emptivly make a copy
va_list args_cp;
va_copy(args_cp, args);
std::size_t size = 256;
char local_buff[256];
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wformat-nonliteral"
#endif // __GNUC__
auto ret = vsnprintf(local_buff, size, msg, args_cp);
#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif // __GNUC__
va_end(args_cp);
// currently there is no error handling for failure, so this is hack.
BM_CHECK(ret >= 0);
if (ret == 0) { // handle empty expansion
return {};
}
if (static_cast<size_t>(ret) < size) {
return local_buff;
}
// we did not provide a long enough buffer on our first attempt.
size = static_cast<size_t>(ret) + 1; // + 1 for the null byte
std::unique_ptr<char[]> buff(new char[size]);
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wformat-nonliteral"
#endif // __GNUC__
ret = vsnprintf(buff.get(), size, msg, args);
#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif // __GNUC__
BM_CHECK(ret > 0 && (static_cast<size_t>(ret)) < size);
return buff.get();
}
std::string FormatString(const char* msg, ...) {
va_list args;
va_start(args, msg);
auto tmp = FormatString(msg, args);
va_end(args);
return tmp;
}
void ColorPrintf(std::ostream& out, LogColor color, const char* fmt, ...) {
va_list args;
va_start(args, fmt);
ColorPrintf(out, color, fmt, args);
va_end(args);
}
void ColorPrintf(std::ostream& out, LogColor color, const char* fmt,
va_list args) {
#ifdef BENCHMARK_OS_WINDOWS
((void)out); // suppress unused warning
const HANDLE stdout_handle = GetStdHandle(STD_OUTPUT_HANDLE);
// Gets the current text color.
CONSOLE_SCREEN_BUFFER_INFO buffer_info;
GetConsoleScreenBufferInfo(stdout_handle, &buffer_info);
const WORD original_color_attrs = buffer_info.wAttributes;
// We need to flush the stream buffers into the console before each
// SetConsoleTextAttribute call lest it affect the text that is already
// printed but has not yet reached the console.
out.flush();
const WORD original_background_attrs =
original_color_attrs & (BACKGROUND_RED | BACKGROUND_GREEN |
BACKGROUND_BLUE | BACKGROUND_INTENSITY);
SetConsoleTextAttribute(stdout_handle, GetPlatformColorCode(color) |
FOREGROUND_INTENSITY |
original_background_attrs);
out << FormatString(fmt, args);
out.flush();
// Restores the text and background color.
SetConsoleTextAttribute(stdout_handle, original_color_attrs);
#else
const char* color_code = GetPlatformColorCode(color);
if (color_code != nullptr) {
out << FormatString("\033[0;3%sm", color_code);
}
out << FormatString(fmt, args) << "\033[m";
#endif
}
bool IsColorTerminal() {
#if BENCHMARK_OS_WINDOWS
// On Windows the TERM variable is usually not set, but the
// console there does support colors.
return 0 != _isatty(_fileno(stdout));
#else
// On non-Windows platforms, we rely on the TERM variable. This list of
// supported TERM values is copied from Google Test:
// <https://github.com/google/googletest/blob/v1.13.0/googletest/src/gtest.cc#L3225-L3259>.
const char* const SUPPORTED_TERM_VALUES[] = {
"xterm",
"xterm-color",
"xterm-256color",
"screen",
"screen-256color",
"tmux",
"tmux-256color",
"rxvt-unicode",
"rxvt-unicode-256color",
"linux",
"cygwin",
"xterm-kitty",
"alacritty",
"foot",
"foot-extra",
"wezterm",
};
const char* const term = getenv("TERM");
bool term_supports_color = false;
for (const char* candidate : SUPPORTED_TERM_VALUES) {
if ((term != nullptr) && 0 == strcmp(term, candidate)) {
term_supports_color = true;
break;
}
}
return 0 != isatty(fileno(stdout)) && term_supports_color;
#endif // BENCHMARK_OS_WINDOWS
}
} // end namespace benchmark

View File

@@ -0,0 +1,33 @@
#ifndef BENCHMARK_COLORPRINT_H_
#define BENCHMARK_COLORPRINT_H_
#include <cstdarg>
#include <iostream>
#include <string>
namespace benchmark {
enum LogColor {
COLOR_DEFAULT,
COLOR_RED,
COLOR_GREEN,
COLOR_YELLOW,
COLOR_BLUE,
COLOR_MAGENTA,
COLOR_CYAN,
COLOR_WHITE
};
std::string FormatString(const char* msg, va_list args);
std::string FormatString(const char* msg, ...);
void ColorPrintf(std::ostream& out, LogColor color, const char* fmt,
va_list args);
void ColorPrintf(std::ostream& out, LogColor color, const char* fmt, ...);
// Returns true if stdout appears to be a terminal that supports colored
// output, false otherwise.
bool IsColorTerminal();
} // end namespace benchmark
#endif // BENCHMARK_COLORPRINT_H_

View File

@@ -0,0 +1,321 @@
// Copyright 2015 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "commandlineflags.h"
#include <algorithm>
#include <cctype>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <limits>
#include <map>
#include <utility>
#include "../src/string_util.h"
namespace benchmark {
namespace {
// Parses 'str' for a 32-bit signed integer. If successful, writes
// the result to *value and returns true; otherwise leaves *value
// unchanged and returns false.
bool ParseInt32(const std::string& src_text, const char* str, int32_t* value) {
// Parses the environment variable as a decimal integer.
char* end = nullptr;
const long long_value = strtol(str, &end, 10); // NOLINT
// Has strtol() consumed all characters in the string?
if (*end != '\0') {
// No - an invalid character was encountered.
std::cerr << src_text << " is expected to be a 32-bit integer, "
<< "but actually has value \"" << str << "\".\n";
return false;
}
// Is the parsed value in the range of an Int32?
const int32_t result = static_cast<int32_t>(long_value);
if (long_value == std::numeric_limits<long>::max() ||
long_value == std::numeric_limits<long>::min() ||
// The parsed value overflows as a long. (strtol() returns
// LONG_MAX or LONG_MIN when the input overflows.)
result != long_value
// The parsed value overflows as an Int32.
) {
std::cerr << src_text << " is expected to be a 32-bit integer, "
<< "but actually has value \"" << str << "\", "
<< "which overflows.\n";
return false;
}
*value = result;
return true;
}
// Parses 'str' for a double. If successful, writes the result to *value and
// returns true; otherwise leaves *value unchanged and returns false.
bool ParseDouble(const std::string& src_text, const char* str, double* value) {
// Parses the environment variable as a decimal integer.
char* end = nullptr;
const double double_value = strtod(str, &end); // NOLINT
// Has strtol() consumed all characters in the string?
if (*end != '\0') {
// No - an invalid character was encountered.
std::cerr << src_text << " is expected to be a double, "
<< "but actually has value \"" << str << "\".\n";
return false;
}
*value = double_value;
return true;
}
// Parses 'str' into KV pairs. If successful, writes the result to *value and
// returns true; otherwise leaves *value unchanged and returns false.
bool ParseKvPairs(const std::string& src_text, const char* str,
std::map<std::string, std::string>* value) {
std::map<std::string, std::string> kvs;
for (const auto& kvpair : StrSplit(str, ',')) {
const auto kv = StrSplit(kvpair, '=');
if (kv.size() != 2) {
std::cerr << src_text << " is expected to be a comma-separated list of "
<< "<key>=<value> strings, but actually has value \"" << str
<< "\".\n";
return false;
}
if (!kvs.emplace(kv[0], kv[1]).second) {
std::cerr << src_text << " is expected to contain unique keys but key \""
<< kv[0] << "\" was repeated.\n";
return false;
}
}
*value = kvs;
return true;
}
// Returns the name of the environment variable corresponding to the
// given flag. For example, FlagToEnvVar("foo") will return
// "BENCHMARK_FOO" in the open-source version.
std::string FlagToEnvVar(const char* flag) {
const std::string flag_str(flag);
std::string env_var;
for (size_t i = 0; i != flag_str.length(); ++i) {
env_var += static_cast<char>(::toupper(flag_str.c_str()[i]));
}
return env_var;
}
} // namespace
BENCHMARK_EXPORT
bool BoolFromEnv(const char* flag, bool default_val) {
const std::string env_var = FlagToEnvVar(flag);
const char* const value_str = getenv(env_var.c_str());
return value_str == nullptr ? default_val : IsTruthyFlagValue(value_str);
}
BENCHMARK_EXPORT
int32_t Int32FromEnv(const char* flag, int32_t default_val) {
const std::string env_var = FlagToEnvVar(flag);
const char* const value_str = getenv(env_var.c_str());
int32_t value = default_val;
if (value_str == nullptr ||
!ParseInt32(std::string("Environment variable ") + env_var, value_str,
&value)) {
return default_val;
}
return value;
}
BENCHMARK_EXPORT
double DoubleFromEnv(const char* flag, double default_val) {
const std::string env_var = FlagToEnvVar(flag);
const char* const value_str = getenv(env_var.c_str());
double value = default_val;
if (value_str == nullptr ||
!ParseDouble(std::string("Environment variable ") + env_var, value_str,
&value)) {
return default_val;
}
return value;
}
BENCHMARK_EXPORT
const char* StringFromEnv(const char* flag, const char* default_val) {
const std::string env_var = FlagToEnvVar(flag);
const char* const value = getenv(env_var.c_str());
return value == nullptr ? default_val : value;
}
BENCHMARK_EXPORT
std::map<std::string, std::string> KvPairsFromEnv(
const char* flag, std::map<std::string, std::string> default_val) {
const std::string env_var = FlagToEnvVar(flag);
const char* const value_str = getenv(env_var.c_str());
if (value_str == nullptr) {
return default_val;
}
std::map<std::string, std::string> value;
if (!ParseKvPairs("Environment variable " + env_var, value_str, &value)) {
return default_val;
}
return value;
}
// Parses a string as a command line flag. The string should have
// the format "--flag=value". When def_optional is true, the "=value"
// part can be omitted.
//
// Returns the value of the flag, or nullptr if the parsing failed.
const char* ParseFlagValue(const char* str, const char* flag,
bool def_optional) {
// str and flag must not be nullptr.
if (str == nullptr || flag == nullptr) {
return nullptr;
}
// The flag must start with "--".
const std::string flag_str = std::string("--") + std::string(flag);
const size_t flag_len = flag_str.length();
if (strncmp(str, flag_str.c_str(), flag_len) != 0) {
return nullptr;
}
// Skips the flag name.
const char* flag_end = str + flag_len;
// When def_optional is true, it's OK to not have a "=value" part.
if (def_optional && (flag_end[0] == '\0')) {
return flag_end;
}
// If def_optional is true and there are more characters after the
// flag name, or if def_optional is false, there must be a '=' after
// the flag name.
if (flag_end[0] != '=') {
return nullptr;
}
// Returns the string after "=".
return flag_end + 1;
}
BENCHMARK_EXPORT
bool ParseBoolFlag(const char* str, const char* flag, bool* value) {
// Gets the value of the flag as a string.
const char* const value_str = ParseFlagValue(str, flag, true);
// Aborts if the parsing failed.
if (value_str == nullptr) {
return false;
}
// Converts the string value to a bool.
*value = IsTruthyFlagValue(value_str);
return true;
}
BENCHMARK_EXPORT
bool ParseInt32Flag(const char* str, const char* flag, int32_t* value) {
// Gets the value of the flag as a string.
const char* const value_str = ParseFlagValue(str, flag, false);
// Aborts if the parsing failed.
if (value_str == nullptr) {
return false;
}
// Sets *value to the value of the flag.
return ParseInt32(std::string("The value of flag --") + flag, value_str,
value);
}
BENCHMARK_EXPORT
bool ParseDoubleFlag(const char* str, const char* flag, double* value) {
// Gets the value of the flag as a string.
const char* const value_str = ParseFlagValue(str, flag, false);
// Aborts if the parsing failed.
if (value_str == nullptr) {
return false;
}
// Sets *value to the value of the flag.
return ParseDouble(std::string("The value of flag --") + flag, value_str,
value);
}
BENCHMARK_EXPORT
bool ParseStringFlag(const char* str, const char* flag, std::string* value) {
// Gets the value of the flag as a string.
const char* const value_str = ParseFlagValue(str, flag, false);
// Aborts if the parsing failed.
if (value_str == nullptr) {
return false;
}
*value = value_str;
return true;
}
BENCHMARK_EXPORT
bool ParseKeyValueFlag(const char* str, const char* flag,
std::map<std::string, std::string>* value) {
const char* const value_str = ParseFlagValue(str, flag, false);
if (value_str == nullptr) {
return false;
}
for (const auto& kvpair : StrSplit(value_str, ',')) {
const auto kv = StrSplit(kvpair, '=');
if (kv.size() != 2) {
return false;
}
value->emplace(kv[0], kv[1]);
}
return true;
}
BENCHMARK_EXPORT
bool IsFlag(const char* str, const char* flag) {
return (ParseFlagValue(str, flag, true) != nullptr);
}
BENCHMARK_EXPORT
bool IsTruthyFlagValue(const std::string& value) {
if (value.size() == 1) {
char v = value[0];
return isalnum(v) &&
!(v == '0' || v == 'f' || v == 'F' || v == 'n' || v == 'N');
}
if (!value.empty()) {
std::string value_lower(value);
std::transform(value_lower.begin(), value_lower.end(), value_lower.begin(),
[](char c) { return static_cast<char>(::tolower(c)); });
return !(value_lower == "false" || value_lower == "no" ||
value_lower == "off");
}
return true;
}
} // end namespace benchmark

View File

@@ -0,0 +1,137 @@
#ifndef BENCHMARK_COMMANDLINEFLAGS_H_
#define BENCHMARK_COMMANDLINEFLAGS_H_
#include <cstdint>
#include <map>
#include <string>
#include "benchmark/export.h"
// Macro for referencing flags.
#define FLAG(name) FLAGS_##name
// Macros for declaring flags.
// NOLINTBEGIN(cppcoreguidelines-avoid-non-const-global-variables)
#define BM_DECLARE_bool(name) BENCHMARK_EXPORT extern bool FLAG(name)
#define BM_DECLARE_int32(name) BENCHMARK_EXPORT extern int32_t FLAG(name)
#define BM_DECLARE_double(name) BENCHMARK_EXPORT extern double FLAG(name)
#define BM_DECLARE_string(name) BENCHMARK_EXPORT extern std::string FLAG(name)
#define BM_DECLARE_kvpairs(name) \
BENCHMARK_EXPORT extern std::map<std::string, std::string> FLAG(name)
// NOLINTEND(cppcoreguidelines-avoid-non-const-global-variables)
// Macros for defining flags.
// NOLINTBEGIN(cppcoreguidelines-avoid-non-const-global-variables)
#define BM_DEFINE_bool(name, default_val) \
BENCHMARK_EXPORT bool FLAG(name) = benchmark::BoolFromEnv(#name, default_val)
#define BM_DEFINE_int32(name, default_val) \
BENCHMARK_EXPORT int32_t FLAG(name) = \
benchmark::Int32FromEnv(#name, default_val)
#define BM_DEFINE_double(name, default_val) \
BENCHMARK_EXPORT double FLAG(name) = \
benchmark::DoubleFromEnv(#name, default_val)
#define BM_DEFINE_string(name, default_val) \
BENCHMARK_EXPORT std::string FLAG(name) = \
benchmark::StringFromEnv(#name, default_val)
#define BM_DEFINE_kvpairs(name, default_val) \
BENCHMARK_EXPORT std::map<std::string, std::string> FLAG(name) = \
benchmark::KvPairsFromEnv(#name, default_val)
// NOLINTEND(cppcoreguidelines-avoid-non-const-global-variables)
namespace benchmark {
// Parses a bool from the environment variable corresponding to the given flag.
//
// If the variable exists, returns IsTruthyFlagValue() value; if not,
// returns the given default value.
BENCHMARK_EXPORT
bool BoolFromEnv(const char* flag, bool default_val);
// Parses an Int32 from the environment variable corresponding to the given
// flag.
//
// If the variable exists, returns ParseInt32() value; if not, returns
// the given default value.
BENCHMARK_EXPORT
int32_t Int32FromEnv(const char* flag, int32_t default_val);
// Parses an Double from the environment variable corresponding to the given
// flag.
//
// If the variable exists, returns ParseDouble(); if not, returns
// the given default value.
BENCHMARK_EXPORT
double DoubleFromEnv(const char* flag, double default_val);
// Parses a string from the environment variable corresponding to the given
// flag.
//
// If variable exists, returns its value; if not, returns
// the given default value.
BENCHMARK_EXPORT
const char* StringFromEnv(const char* flag, const char* default_val);
// Parses a set of kvpairs from the environment variable corresponding to the
// given flag.
//
// If variable exists, returns its value; if not, returns
// the given default value.
BENCHMARK_EXPORT
std::map<std::string, std::string> KvPairsFromEnv(
const char* flag, std::map<std::string, std::string> default_val);
// Parses a string for a bool flag, in the form of either
// "--flag=value" or "--flag".
//
// In the former case, the value is taken as true if it passes IsTruthyValue().
//
// In the latter case, the value is taken as true.
//
// On success, stores the value of the flag in *value, and returns
// true. On failure, returns false without changing *value.
BENCHMARK_EXPORT
bool ParseBoolFlag(const char* str, const char* flag, bool* value);
// Parses a string for an Int32 flag, in the form of "--flag=value".
//
// On success, stores the value of the flag in *value, and returns
// true. On failure, returns false without changing *value.
BENCHMARK_EXPORT
bool ParseInt32Flag(const char* str, const char* flag, int32_t* value);
// Parses a string for a Double flag, in the form of "--flag=value".
//
// On success, stores the value of the flag in *value, and returns
// true. On failure, returns false without changing *value.
BENCHMARK_EXPORT
bool ParseDoubleFlag(const char* str, const char* flag, double* value);
// Parses a string for a string flag, in the form of "--flag=value".
//
// On success, stores the value of the flag in *value, and returns
// true. On failure, returns false without changing *value.
BENCHMARK_EXPORT
bool ParseStringFlag(const char* str, const char* flag, std::string* value);
// Parses a string for a kvpairs flag in the form "--flag=key=value,key=value"
//
// On success, stores the value of the flag in *value and returns true. On
// failure returns false, though *value may have been mutated.
BENCHMARK_EXPORT
bool ParseKeyValueFlag(const char* str, const char* flag,
std::map<std::string, std::string>* value);
// Returns true if the string matches the flag.
BENCHMARK_EXPORT
bool IsFlag(const char* str, const char* flag);
// Returns true unless value starts with one of: '0', 'f', 'F', 'n' or 'N', or
// some non-alphanumeric character. Also returns false if the value matches
// one of 'no', 'false', 'off' (case-insensitive). As a special case, also
// returns true if value is the empty string.
BENCHMARK_EXPORT
bool IsTruthyFlagValue(const std::string& value);
} // end namespace benchmark
#endif // BENCHMARK_COMMANDLINEFLAGS_H_

View File

@@ -0,0 +1,257 @@
// Copyright 2016 Ismael Jimenez Martinez. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Source project : https://github.com/ismaelJimenez/cpp.leastsq
// Adapted to be used with google benchmark
#include "complexity.h"
#include <algorithm>
#include <cmath>
#include "benchmark/benchmark.h"
#include "check.h"
namespace benchmark {
// Internal function to calculate the different scalability forms
BigOFunc* FittingCurve(BigO complexity) {
switch (complexity) {
case oN:
return [](IterationCount n) -> double { return static_cast<double>(n); };
case oNSquared:
return [](IterationCount n) -> double { return std::pow(n, 2); };
case oNCubed:
return [](IterationCount n) -> double { return std::pow(n, 3); };
case oLogN:
return [](IterationCount n) -> double {
return std::log2(static_cast<double>(n));
};
case oNLogN:
return [](IterationCount n) -> double {
return static_cast<double>(n) * std::log2(static_cast<double>(n));
};
case o1:
default:
return [](IterationCount) { return 1.0; };
}
}
// Function to return an string for the calculated complexity
std::string GetBigOString(BigO complexity) {
switch (complexity) {
case oN:
return "N";
case oNSquared:
return "N^2";
case oNCubed:
return "N^3";
case oLogN:
return "lgN";
case oNLogN:
return "NlgN";
case o1:
return "(1)";
default:
return "f(N)";
}
}
// Find the coefficient for the high-order term in the running time, by
// minimizing the sum of squares of relative error, for the fitting curve
// given by the lambda expression.
// - n : Vector containing the size of the benchmark tests.
// - time : Vector containing the times for the benchmark tests.
// - fitting_curve : lambda expression (e.g. [](ComplexityN n) {return n; };).
// For a deeper explanation on the algorithm logic, please refer to
// https://en.wikipedia.org/wiki/Least_squares#Least_squares,_regression_analysis_and_statistics
LeastSq MinimalLeastSq(const std::vector<ComplexityN>& n,
const std::vector<double>& time,
BigOFunc* fitting_curve) {
double sigma_gn_squared = 0.0;
double sigma_time = 0.0;
double sigma_time_gn = 0.0;
// Calculate least square fitting parameter
for (size_t i = 0; i < n.size(); ++i) {
double gn_i = fitting_curve(n[i]);
sigma_gn_squared += gn_i * gn_i;
sigma_time += time[i];
sigma_time_gn += time[i] * gn_i;
}
LeastSq result;
result.complexity = oLambda;
// Calculate complexity.
result.coef = sigma_time_gn / sigma_gn_squared;
// Calculate RMS
double rms = 0.0;
for (size_t i = 0; i < n.size(); ++i) {
double fit = result.coef * fitting_curve(n[i]);
rms += std::pow((time[i] - fit), 2);
}
// Normalized RMS by the mean of the observed values
double mean = sigma_time / static_cast<double>(n.size());
result.rms = std::sqrt(rms / static_cast<double>(n.size())) / mean;
return result;
}
// Find the coefficient for the high-order term in the running time, by
// minimizing the sum of squares of relative error.
// - n : Vector containing the size of the benchmark tests.
// - time : Vector containing the times for the benchmark tests.
// - complexity : If different than oAuto, the fitting curve will stick to
// this one. If it is oAuto, it will be calculated the best
// fitting curve.
LeastSq MinimalLeastSq(const std::vector<ComplexityN>& n,
const std::vector<double>& time, const BigO complexity) {
BM_CHECK_EQ(n.size(), time.size());
BM_CHECK_GE(n.size(), 2); // Do not compute fitting curve is less than two
// benchmark runs are given
BM_CHECK_NE(complexity, oNone);
LeastSq best_fit;
if (complexity == oAuto) {
std::vector<BigO> fit_curves = {oLogN, oN, oNLogN, oNSquared, oNCubed};
// Take o1 as default best fitting curve
best_fit = MinimalLeastSq(n, time, FittingCurve(o1));
best_fit.complexity = o1;
// Compute all possible fitting curves and stick to the best one
for (const auto& fit : fit_curves) {
LeastSq current_fit = MinimalLeastSq(n, time, FittingCurve(fit));
if (current_fit.rms < best_fit.rms) {
best_fit = current_fit;
best_fit.complexity = fit;
}
}
} else {
best_fit = MinimalLeastSq(n, time, FittingCurve(complexity));
best_fit.complexity = complexity;
}
return best_fit;
}
std::vector<BenchmarkReporter::Run> ComputeBigO(
const std::vector<BenchmarkReporter::Run>& reports) {
typedef BenchmarkReporter::Run Run;
std::vector<Run> results;
if (reports.size() < 2) {
return results;
}
// Accumulators.
std::vector<ComplexityN> n;
std::vector<double> real_time;
std::vector<double> cpu_time;
// Populate the accumulators.
for (const Run& run : reports) {
BM_CHECK_GT(run.complexity_n, 0)
<< "Did you forget to call SetComplexityN?";
n.push_back(run.complexity_n);
real_time.push_back(run.real_accumulated_time /
static_cast<double>(run.iterations));
cpu_time.push_back(run.cpu_accumulated_time /
static_cast<double>(run.iterations));
}
LeastSq result_cpu;
LeastSq result_real;
if (reports[0].complexity == oLambda) {
result_cpu = MinimalLeastSq(n, cpu_time, reports[0].complexity_lambda);
result_real = MinimalLeastSq(n, real_time, reports[0].complexity_lambda);
} else {
const BigO* InitialBigO = &reports[0].complexity;
const bool use_real_time_for_initial_big_o =
reports[0].use_real_time_for_initial_big_o;
if (use_real_time_for_initial_big_o) {
result_real = MinimalLeastSq(n, real_time, *InitialBigO);
InitialBigO = &result_real.complexity;
// The Big-O complexity for CPU time must have the same Big-O function!
}
result_cpu = MinimalLeastSq(n, cpu_time, *InitialBigO);
InitialBigO = &result_cpu.complexity;
if (!use_real_time_for_initial_big_o) {
result_real = MinimalLeastSq(n, real_time, *InitialBigO);
}
}
// Drop the 'args' when reporting complexity.
auto run_name = reports[0].run_name;
run_name.args.clear();
// Get the data from the accumulator to BenchmarkReporter::Run's.
Run big_o;
big_o.run_name = run_name;
big_o.family_index = reports[0].family_index;
big_o.per_family_instance_index = reports[0].per_family_instance_index;
big_o.run_type = BenchmarkReporter::Run::RT_Aggregate;
big_o.repetitions = reports[0].repetitions;
big_o.repetition_index = Run::no_repetition_index;
big_o.threads = reports[0].threads;
big_o.aggregate_name = "BigO";
big_o.aggregate_unit = StatisticUnit::kTime;
big_o.report_label = reports[0].report_label;
big_o.iterations = 0;
big_o.real_accumulated_time = result_real.coef;
big_o.cpu_accumulated_time = result_cpu.coef;
big_o.report_big_o = true;
big_o.complexity = result_cpu.complexity;
// All the time results are reported after being multiplied by the
// time unit multiplier. But since RMS is a relative quantity it
// should not be multiplied at all. So, here, we _divide_ it by the
// multiplier so that when it is multiplied later the result is the
// correct one.
double multiplier = GetTimeUnitMultiplier(reports[0].time_unit);
// Only add label to mean/stddev if it is same for all runs
Run rms;
rms.run_name = run_name;
rms.family_index = reports[0].family_index;
rms.per_family_instance_index = reports[0].per_family_instance_index;
rms.run_type = BenchmarkReporter::Run::RT_Aggregate;
rms.aggregate_name = "RMS";
rms.aggregate_unit = StatisticUnit::kPercentage;
rms.report_label = big_o.report_label;
rms.iterations = 0;
rms.repetition_index = Run::no_repetition_index;
rms.repetitions = reports[0].repetitions;
rms.threads = reports[0].threads;
rms.real_accumulated_time = result_real.rms / multiplier;
rms.cpu_accumulated_time = result_cpu.rms / multiplier;
rms.report_rms = true;
rms.complexity = result_cpu.complexity;
// don't forget to keep the time unit, or we won't be able to
// recover the correct value.
rms.time_unit = reports[0].time_unit;
results.push_back(big_o);
results.push_back(rms);
return results;
}
} // end namespace benchmark

View File

@@ -0,0 +1,55 @@
// Copyright 2016 Ismael Jimenez Martinez. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Source project : https://github.com/ismaelJimenez/cpp.leastsq
// Adapted to be used with google benchmark
#ifndef COMPLEXITY_H_
#define COMPLEXITY_H_
#include <string>
#include <vector>
#include "benchmark/benchmark.h"
namespace benchmark {
// Return a vector containing the bigO and RMS information for the specified
// list of reports. If 'reports.size() < 2' an empty vector is returned.
std::vector<BenchmarkReporter::Run> ComputeBigO(
const std::vector<BenchmarkReporter::Run>& reports);
// This data structure will contain the result returned by MinimalLeastSq
// - coef : Estimated coefficient for the high-order term as
// interpolated from data.
// - rms : Normalized Root Mean Squared Error.
// - complexity : Scalability form (e.g. oN, oNLogN). In case a scalability
// form has been provided to MinimalLeastSq this will return
// the same value. In case BigO::oAuto has been selected, this
// parameter will return the best fitting curve detected.
struct LeastSq {
LeastSq() : coef(0.0), rms(0.0), complexity(oNone) {}
double coef;
double rms;
BigO complexity;
};
// Function to return an string for the calculated complexity
std::string GetBigOString(BigO complexity);
} // end namespace benchmark
#endif // COMPLEXITY_H_

View File

@@ -0,0 +1,212 @@
// Copyright 2015 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <algorithm>
#include <cstdint>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <string>
#include <tuple>
#include <vector>
#include "benchmark/benchmark.h"
#include "check.h"
#include "colorprint.h"
#include "commandlineflags.h"
#include "complexity.h"
#include "counter.h"
#include "internal_macros.h"
#include "string_util.h"
#include "timers.h"
namespace benchmark {
BENCHMARK_EXPORT
bool ConsoleReporter::ReportContext(const Context& context) {
name_field_width_ = context.name_field_width;
printed_header_ = false;
prev_counters_.clear();
PrintBasicContext(&GetErrorStream(), context);
#ifdef BENCHMARK_OS_WINDOWS
if ((output_options_ & OO_Color)) {
auto stdOutBuf = std::cout.rdbuf();
auto outStreamBuf = GetOutputStream().rdbuf();
if (stdOutBuf != outStreamBuf) {
GetErrorStream()
<< "Color printing is only supported for stdout on windows."
" Disabling color printing\n";
output_options_ = static_cast<OutputOptions>(output_options_ & ~OO_Color);
}
}
#endif
return true;
}
BENCHMARK_EXPORT
void ConsoleReporter::PrintHeader(const Run& run) {
std::string str =
FormatString("%-*s %13s %15s %12s", static_cast<int>(name_field_width_),
"Benchmark", "Time", "CPU", "Iterations");
if (!run.counters.empty()) {
if ((output_options_ & OO_Tabular) != 0) {
for (auto const& c : run.counters) {
str += FormatString(" %10s", c.first.c_str());
}
} else {
str += " UserCounters...";
}
}
std::string line = std::string(str.length(), '-');
GetOutputStream() << line << "\n" << str << "\n" << line << "\n";
}
BENCHMARK_EXPORT
void ConsoleReporter::ReportRuns(const std::vector<Run>& reports) {
for (const auto& run : reports) {
// print the header:
// --- if none was printed yet
bool print_header = !printed_header_;
// --- or if the format is tabular and this run
// has different fields from the prev header
print_header |= ((output_options_ & OO_Tabular) != 0) &&
(!internal::SameNames(run.counters, prev_counters_));
if (print_header) {
printed_header_ = true;
prev_counters_ = run.counters;
PrintHeader(run);
}
// As an alternative to printing the headers like this, we could sort
// the benchmarks by header and then print. But this would require
// waiting for the full results before printing, or printing twice.
PrintRunData(run);
}
}
static void IgnoreColorPrint(std::ostream& out, LogColor /*unused*/,
const char* fmt, ...) {
va_list args;
va_start(args, fmt);
out << FormatString(fmt, args);
va_end(args);
}
static std::string FormatTime(double time) {
// For the time columns of the console printer 13 digits are reserved. One of
// them is a space and max two of them are the time unit (e.g ns). That puts
// us at 10 digits usable for the number.
// Align decimal places...
if (time < 1.0) {
return FormatString("%10.3f", time);
}
if (time < 10.0) {
return FormatString("%10.2f", time);
}
if (time < 100.0) {
return FormatString("%10.1f", time);
}
// Assuming the time is at max 9.9999e+99 and we have 10 digits for the
// number, we get 10-1(.)-1(e)-1(sign)-2(exponent) = 5 digits to print.
if (time > 9999999999 /*max 10 digit number*/) {
return FormatString("%1.4e", time);
}
return FormatString("%10.0f", time);
}
BENCHMARK_EXPORT
void ConsoleReporter::PrintRunData(const Run& result) {
typedef void(PrinterFn)(std::ostream&, LogColor, const char*, ...);
auto& Out = GetOutputStream();
PrinterFn* printer = (output_options_ & OO_Color) != 0
? static_cast<PrinterFn*>(ColorPrintf)
: IgnoreColorPrint;
auto name_color =
(result.report_big_o || result.report_rms) ? COLOR_BLUE : COLOR_GREEN;
printer(Out, name_color, "%-*s ", name_field_width_,
result.benchmark_name().c_str());
if (internal::SkippedWithError == result.skipped) {
printer(Out, COLOR_RED, "ERROR OCCURRED: \'%s\'",
result.skip_message.c_str());
printer(Out, COLOR_DEFAULT, "\n");
return;
}
if (internal::SkippedWithMessage == result.skipped) {
printer(Out, COLOR_WHITE, "SKIPPED: \'%s\'", result.skip_message.c_str());
printer(Out, COLOR_DEFAULT, "\n");
return;
}
const double real_time = result.GetAdjustedRealTime();
const double cpu_time = result.GetAdjustedCPUTime();
const std::string real_time_str = FormatTime(real_time);
const std::string cpu_time_str = FormatTime(cpu_time);
if (result.report_big_o) {
std::string big_o = GetBigOString(result.complexity);
printer(Out, COLOR_YELLOW, "%10.2f %-4s %10.2f %-4s ", real_time,
big_o.c_str(), cpu_time, big_o.c_str());
} else if (result.report_rms) {
printer(Out, COLOR_YELLOW, "%10.0f %-4s %10.0f %-4s ", real_time * 100, "%",
cpu_time * 100, "%");
} else if (result.run_type != Run::RT_Aggregate ||
result.aggregate_unit == StatisticUnit::kTime) {
const char* timeLabel = GetTimeUnitString(result.time_unit);
printer(Out, COLOR_YELLOW, "%s %-4s %s %-4s ", real_time_str.c_str(),
timeLabel, cpu_time_str.c_str(), timeLabel);
} else {
assert(result.aggregate_unit == StatisticUnit::kPercentage);
printer(Out, COLOR_YELLOW, "%10.2f %-4s %10.2f %-4s ",
(100. * result.real_accumulated_time), "%",
(100. * result.cpu_accumulated_time), "%");
}
if (!result.report_big_o && !result.report_rms) {
printer(Out, COLOR_CYAN, "%10lld", result.iterations);
}
for (const auto& c : result.counters) {
const std::size_t cNameLen =
std::max(static_cast<std::size_t>(10), c.first.length());
std::string s;
const char* unit = "";
if (result.run_type == Run::RT_Aggregate &&
result.aggregate_unit == StatisticUnit::kPercentage) {
s = StrFormat("%.2f", 100. * c.second.value);
unit = "%";
} else {
s = HumanReadableNumber(c.second.value, c.second.oneK);
if ((c.second.flags & Counter::kIsRate) != 0) {
unit = (c.second.flags & Counter::kInvert) != 0 ? "s" : "/s";
}
}
if ((output_options_ & OO_Tabular) != 0) {
printer(Out, COLOR_DEFAULT, " %*s%s", cNameLen - strlen(unit), s.c_str(),
unit);
} else {
printer(Out, COLOR_DEFAULT, " %s=%s%s", c.first.c_str(), s.c_str(), unit);
}
}
if (!result.report_label.empty()) {
printer(Out, COLOR_DEFAULT, " %s", result.report_label.c_str());
}
printer(Out, COLOR_DEFAULT, "\n");
}
} // end namespace benchmark

View File

@@ -0,0 +1,82 @@
// Copyright 2015 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "counter.h"
namespace benchmark {
namespace internal {
double Finish(Counter const& c, IterationCount iterations, double cpu_time,
double num_threads) {
double v = c.value;
if ((c.flags & Counter::kIsRate) != 0) {
v /= cpu_time;
}
if ((c.flags & Counter::kAvgThreads) != 0) {
v /= num_threads;
}
if ((c.flags & Counter::kIsIterationInvariant) != 0) {
v *= static_cast<double>(iterations);
}
if ((c.flags & Counter::kAvgIterations) != 0) {
v /= static_cast<double>(iterations);
}
if ((c.flags & Counter::kInvert) != 0) { // Invert is *always* last.
v = 1.0 / v;
}
return v;
}
void Finish(UserCounters* l, IterationCount iterations, double cpu_time,
double num_threads) {
for (auto& c : *l) {
c.second.value = Finish(c.second, iterations, cpu_time, num_threads);
}
}
void Increment(UserCounters* l, UserCounters const& r) {
// add counters present in both or just in *l
for (auto& c : *l) {
auto it = r.find(c.first);
if (it != r.end()) {
c.second.value = c.second + it->second;
}
}
// add counters present in r, but not in *l
for (auto const& tc : r) {
auto it = l->find(tc.first);
if (it == l->end()) {
(*l)[tc.first] = tc.second;
}
}
}
bool SameNames(UserCounters const& l, UserCounters const& r) {
if (&l == &r) {
return true;
}
if (l.size() != r.size()) {
return false;
}
for (auto const& c : l) {
if (r.find(c.first) == r.end()) {
return false;
}
}
return true;
}
} // end namespace internal
} // end namespace benchmark

View File

@@ -0,0 +1,32 @@
// Copyright 2015 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef BENCHMARK_COUNTER_H_
#define BENCHMARK_COUNTER_H_
#include "benchmark/benchmark.h"
namespace benchmark {
// these counter-related functions are hidden to reduce API surface.
namespace internal {
void Finish(UserCounters* l, IterationCount iterations, double time,
double num_threads);
void Increment(UserCounters* l, UserCounters const& r);
bool SameNames(UserCounters const& l, UserCounters const& r);
} // end namespace internal
} // end namespace benchmark
#endif // BENCHMARK_COUNTER_H_

View File

@@ -0,0 +1,175 @@
// Copyright 2015 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <algorithm>
#include <cstdint>
#include <iostream>
#include <string>
#include <tuple>
#include <vector>
#include "benchmark/benchmark.h"
#include "check.h"
#include "complexity.h"
#include "string_util.h"
#include "timers.h"
// File format reference: http://edoceo.com/utilitas/csv-file-format.
namespace benchmark {
namespace {
std::vector<std::string> elements = {
"name", "iterations", "real_time", "cpu_time",
"time_unit", "bytes_per_second", "items_per_second", "label",
"error_occurred", "error_message"};
} // namespace
std::string CsvEscape(const std::string& s) {
std::string tmp;
tmp.reserve(s.size() + 2);
for (char c : s) {
switch (c) {
case '"':
tmp += "\"\"";
break;
default:
tmp += c;
break;
}
}
return '"' + tmp + '"';
}
BENCHMARK_EXPORT
bool CSVReporter::ReportContext(const Context& context) {
PrintBasicContext(&GetErrorStream(), context);
return true;
}
BENCHMARK_EXPORT
void CSVReporter::ReportRuns(const std::vector<Run>& reports) {
std::ostream& Out = GetOutputStream();
if (!printed_header_) {
// save the names of all the user counters
for (const auto& run : reports) {
for (const auto& cnt : run.counters) {
if (cnt.first == "bytes_per_second" ||
cnt.first == "items_per_second") {
continue;
}
user_counter_names_.insert(cnt.first);
}
}
// print the header
for (auto B = elements.begin(); B != elements.end();) {
Out << *B++;
if (B != elements.end()) {
Out << ",";
}
}
for (auto B = user_counter_names_.begin();
B != user_counter_names_.end();) {
Out << ",\"" << *B++ << "\"";
}
Out << "\n";
printed_header_ = true;
} else {
// check that all the current counters are saved in the name set
for (const auto& run : reports) {
for (const auto& cnt : run.counters) {
if (cnt.first == "bytes_per_second" ||
cnt.first == "items_per_second") {
continue;
}
BM_CHECK(user_counter_names_.find(cnt.first) !=
user_counter_names_.end())
<< "All counters must be present in each run. "
<< "Counter named \"" << cnt.first
<< "\" was not in a run after being added to the header";
}
}
}
// print results for each run
for (const auto& run : reports) {
PrintRunData(run);
}
}
BENCHMARK_EXPORT
void CSVReporter::PrintRunData(const Run& run) {
std::ostream& Out = GetOutputStream();
Out << CsvEscape(run.benchmark_name()) << ",";
if (run.skipped != 0u) {
Out << std::string(elements.size() - 3, ',');
Out << std::boolalpha << (internal::SkippedWithError == run.skipped) << ",";
Out << CsvEscape(run.skip_message) << "\n";
return;
}
// Do not print iteration on bigO and RMS report
if (!run.report_big_o && !run.report_rms) {
Out << run.iterations;
}
Out << ",";
if (run.run_type != Run::RT_Aggregate ||
run.aggregate_unit == StatisticUnit::kTime) {
Out << run.GetAdjustedRealTime() << ",";
Out << run.GetAdjustedCPUTime() << ",";
} else {
assert(run.aggregate_unit == StatisticUnit::kPercentage);
Out << run.real_accumulated_time << ",";
Out << run.cpu_accumulated_time << ",";
}
// Do not print timeLabel on bigO and RMS report
if (run.report_big_o) {
Out << GetBigOString(run.complexity);
} else if (!run.report_rms &&
run.aggregate_unit != StatisticUnit::kPercentage) {
Out << GetTimeUnitString(run.time_unit);
}
Out << ",";
if (run.counters.find("bytes_per_second") != run.counters.end()) {
Out << run.counters.at("bytes_per_second");
}
Out << ",";
if (run.counters.find("items_per_second") != run.counters.end()) {
Out << run.counters.at("items_per_second");
}
Out << ",";
if (!run.report_label.empty()) {
Out << CsvEscape(run.report_label);
}
Out << ",,"; // for error_occurred and error_message
// Print user counters
for (const auto& ucn : user_counter_names_) {
auto it = run.counters.find(ucn);
if (it == run.counters.end()) {
Out << ",";
} else {
Out << "," << it->second;
}
}
Out << '\n';
}
} // end namespace benchmark

View File

@@ -0,0 +1,255 @@
// ----------------------------------------------------------------------
// CycleClock
// A CycleClock tells you the current time in Cycles. The "time"
// is actually time since power-on. This is like time() but doesn't
// involve a system call and is much more precise.
//
// NOTE: Not all cpu/platform/kernel combinations guarantee that this
// clock increments at a constant rate or is synchronized across all logical
// cpus in a system.
//
// If you need the above guarantees, please consider using a different
// API. There are efforts to provide an interface which provides a millisecond
// granularity and implemented as a memory read. A memory read is generally
// cheaper than the CycleClock for many architectures.
//
// Also, in some out of order CPU implementations, the CycleClock is not
// serializing. So if you're trying to count at cycles granularity, your
// data might be inaccurate due to out of order instruction execution.
// ----------------------------------------------------------------------
#ifndef BENCHMARK_CYCLECLOCK_H_
#define BENCHMARK_CYCLECLOCK_H_
#include <cstdint>
#include "benchmark/benchmark.h"
#include "internal_macros.h"
#if defined(BENCHMARK_OS_MACOSX)
#include <mach/mach_time.h>
#endif
// For MSVC, we want to use '_asm rdtsc' when possible (since it works
// with even ancient MSVC compilers), and when not possible the
// __rdtsc intrinsic, declared in <intrin.h>. Unfortunately, in some
// environments, <windows.h> and <intrin.h> have conflicting
// declarations of some other intrinsics, breaking compilation.
// Therefore, we simply declare __rdtsc ourselves. See also
// http://connect.microsoft.com/VisualStudio/feedback/details/262047
#if defined(COMPILER_MSVC) && !defined(_M_IX86) && !defined(_M_ARM64) && \
!defined(_M_ARM64EC)
extern "C" uint64_t __rdtsc();
#pragma intrinsic(__rdtsc)
#endif
#if !defined(BENCHMARK_OS_WINDOWS) || defined(BENCHMARK_OS_MINGW)
#include <sys/time.h>
#include <time.h>
#endif
#ifdef BENCHMARK_OS_EMSCRIPTEN
#include <emscripten.h>
#endif
namespace benchmark {
// NOTE: only i386 and x86_64 have been well tested.
// PPC, sparc, alpha, and ia64 are based on
// http://peter.kuscsik.com/wordpress/?p=14
// with modifications by m3b. See also
// https://setisvn.ssl.berkeley.edu/svn/lib/fftw-3.0.1/kernel/cycle.h
namespace cycleclock {
// This should return the number of cycles since power-on. Thread-safe.
inline BENCHMARK_ALWAYS_INLINE int64_t Now() {
#if defined(BENCHMARK_OS_MACOSX)
// this goes at the top because we need ALL Macs, regardless of
// architecture, to return the number of "mach time units" that
// have passed since startup. See sysinfo.cc where
// InitializeSystemInfo() sets the supposed cpu clock frequency of
// macs to the number of mach time units per second, not actual
// CPU clock frequency (which can change in the face of CPU
// frequency scaling). Also note that when the Mac sleeps, this
// counter pauses; it does not continue counting, nor does it
// reset to zero.
return static_cast<int64_t>(mach_absolute_time());
#elif defined(BENCHMARK_OS_EMSCRIPTEN)
// this goes above x86-specific code because old versions of Emscripten
// define __x86_64__, although they have nothing to do with it.
return static_cast<int64_t>(emscripten_get_now() * 1e+6);
#elif defined(__i386__)
int64_t ret;
__asm__ volatile("rdtsc" : "=A"(ret));
return ret;
#elif defined(__x86_64__) || defined(__amd64__)
uint64_t low, high;
__asm__ volatile("rdtsc" : "=a"(low), "=d"(high));
return static_cast<int64_t>((high << 32) | low);
#elif defined(__powerpc__) || defined(__ppc__)
// This returns a time-base, which is not always precisely a cycle-count.
#if defined(__powerpc64__) || defined(__ppc64__)
int64_t tb;
asm volatile("mfspr %0, 268" : "=r"(tb));
return tb;
#else
uint32_t tbl, tbu0, tbu1;
asm volatile(
"mftbu %0\n"
"mftb %1\n"
"mftbu %2"
: "=r"(tbu0), "=r"(tbl), "=r"(tbu1));
tbl &= -static_cast<int32_t>(tbu0 == tbu1);
// high 32 bits in tbu1; low 32 bits in tbl (tbu0 is no longer needed)
return (static_cast<uint64_t>(tbu1) << 32) | tbl;
#endif
#elif defined(__sparc__)
int64_t tick;
asm(".byte 0x83, 0x41, 0x00, 0x00");
asm("mov %%g1, %0" : "=r"(tick));
return tick;
#elif defined(__ia64__)
int64_t itc;
asm("mov %0 = ar.itc" : "=r"(itc));
return itc;
#elif defined(COMPILER_MSVC) && defined(_M_IX86)
// Older MSVC compilers (like 7.x) don't seem to support the
// __rdtsc intrinsic properly, so I prefer to use _asm instead
// when I know it will work. Otherwise, I'll use __rdtsc and hope
// the code is being compiled with a non-ancient compiler.
_asm rdtsc
#elif defined(COMPILER_MSVC) && (defined(_M_ARM64) || defined(_M_ARM64EC))
// See // https://docs.microsoft.com/en-us/cpp/intrinsics/arm64-intrinsics
// and https://reviews.llvm.org/D53115
int64_t virtual_timer_value;
virtual_timer_value = _ReadStatusReg(ARM64_CNTVCT);
return virtual_timer_value;
#elif defined(COMPILER_MSVC)
return __rdtsc();
#elif defined(BENCHMARK_OS_NACL)
// Native Client validator on x86/x86-64 allows RDTSC instructions,
// and this case is handled above. Native Client validator on ARM
// rejects MRC instructions (used in the ARM-specific sequence below),
// so we handle it here. Portable Native Client compiles to
// architecture-agnostic bytecode, which doesn't provide any
// cycle counter access mnemonics.
// Native Client does not provide any API to access cycle counter.
// Use clock_gettime(CLOCK_MONOTONIC, ...) instead of gettimeofday
// because is provides nanosecond resolution (which is noticeable at
// least for PNaCl modules running on x86 Mac & Linux).
// Initialize to always return 0 if clock_gettime fails.
struct timespec ts = {0, 0};
clock_gettime(CLOCK_MONOTONIC, &ts);
return static_cast<int64_t>(ts.tv_sec) * 1000000000 + ts.tv_nsec;
#elif defined(__aarch64__)
// System timer of ARMv8 runs at a different frequency than the CPU's.
// The frequency is fixed, typically in the range 1-50MHz. It can be
// read at CNTFRQ special register. We assume the OS has set up
// the virtual timer properly.
int64_t virtual_timer_value;
asm volatile("mrs %0, cntvct_el0" : "=r"(virtual_timer_value));
return virtual_timer_value;
#elif defined(__ARM_ARCH)
// V6 is the earliest arch that has a standard cyclecount
// Native Client validator doesn't allow MRC instructions.
#if (__ARM_ARCH >= 6)
uint32_t pmccntr;
uint32_t pmuseren;
uint32_t pmcntenset;
// Read the user mode perf monitor counter access permissions.
asm volatile("mrc p15, 0, %0, c9, c14, 0" : "=r"(pmuseren));
if (pmuseren & 1) { // Allows reading perfmon counters for user mode code.
asm volatile("mrc p15, 0, %0, c9, c12, 1" : "=r"(pmcntenset));
if (pmcntenset & 0x80000000ul) { // Is it counting?
asm volatile("mrc p15, 0, %0, c9, c13, 0" : "=r"(pmccntr));
// The counter is set up to count every 64th cycle
return static_cast<int64_t>(pmccntr) * 64; // Should optimize to << 6
}
}
#endif
struct timeval tv;
gettimeofday(&tv, nullptr);
return static_cast<int64_t>(tv.tv_sec) * 1000000 + tv.tv_usec;
#elif defined(__mips__) || defined(__m68k__)
// mips apparently only allows rdtsc for superusers, so we fall
// back to gettimeofday. It's possible clock_gettime would be better.
struct timeval tv;
gettimeofday(&tv, nullptr);
return static_cast<int64_t>(tv.tv_sec) * 1000000 + tv.tv_usec;
#elif defined(__loongarch__) || defined(__csky__)
struct timeval tv;
gettimeofday(&tv, nullptr);
return static_cast<int64_t>(tv.tv_sec) * 1000000 + tv.tv_usec;
#elif defined(__s390__) // Covers both s390 and s390x.
// Return the CPU clock.
uint64_t tsc;
#if defined(BENCHMARK_OS_ZOS)
// z/OS HLASM syntax.
asm(" stck %0" : "=m"(tsc) : : "cc");
#else
// Linux on Z syntax.
asm("stck %0" : "=Q"(tsc) : : "cc");
#endif
return tsc;
#elif defined(__riscv) // RISC-V
// Use RDTIME (and RDTIMEH on riscv32).
// RDCYCLE is a privileged instruction since Linux 6.6.
#if __riscv_xlen == 32
uint32_t cycles_lo, cycles_hi0, cycles_hi1;
// This asm also includes the PowerPC overflow handling strategy, as above.
// Implemented in assembly because Clang insisted on branching.
asm volatile(
"rdtimeh %0\n"
"rdtime %1\n"
"rdtimeh %2\n"
"sub %0, %0, %2\n"
"seqz %0, %0\n"
"sub %0, zero, %0\n"
"and %1, %1, %0\n"
: "=r"(cycles_hi0), "=r"(cycles_lo), "=r"(cycles_hi1));
return static_cast<int64_t>((static_cast<uint64_t>(cycles_hi1) << 32) |
cycles_lo);
#else
uint64_t cycles;
asm volatile("rdtime %0" : "=r"(cycles));
return static_cast<int64_t>(cycles);
#endif
#elif defined(__e2k__) || defined(__elbrus__)
struct timeval tv;
gettimeofday(&tv, nullptr);
return static_cast<int64_t>(tv.tv_sec) * 1000000 + tv.tv_usec;
#elif defined(__hexagon__)
uint64_t pcycle;
asm volatile("%0 = C15:14" : "=r"(pcycle));
return static_cast<int64_t>(pcycle);
#elif defined(__alpha__)
// Alpha has a cycle counter, the PCC register, but it is an unsigned 32-bit
// integer and thus wraps every ~4s, making using it for tick counts
// unreliable beyond this time range. The real-time clock is low-precision,
// roughtly ~1ms, but it is the only option that can reasonable count
// indefinitely.
struct timeval tv;
gettimeofday(&tv, nullptr);
return static_cast<int64_t>(tv.tv_sec) * 1000000 + tv.tv_usec;
#elif defined(__hppa__) || defined(__linux__)
// Fallback for all other architectures with a recent Linux kernel, e.g.:
// HP PA-RISC provides a user-readable clock counter (cr16), but
// it's not syncronized across CPUs and only 32-bit wide when programs
// are built as 32-bit binaries.
// Same for SH-4 and possibly others.
// Use clock_gettime(CLOCK_MONOTONIC, ...) instead of gettimeofday
// because is provides nanosecond resolution.
// Initialize to always return 0 if clock_gettime fails.
struct timespec ts = {0, 0};
clock_gettime(CLOCK_MONOTONIC, &ts);
return static_cast<int64_t>(ts.tv_sec) * 1000000000 + ts.tv_nsec;
#else
// The soft failover to a generic implementation is automatic only for ARM.
// For other platforms the developer is expected to make an attempt to create
// a fast implementation and use generic version if nothing better is
// available.
#error You need to define CycleTimer for your OS and CPU
#endif
}
} // end namespace cycleclock
} // end namespace benchmark
#endif // BENCHMARK_CYCLECLOCK_H_

View File

@@ -0,0 +1,111 @@
#ifndef BENCHMARK_INTERNAL_MACROS_H_
#define BENCHMARK_INTERNAL_MACROS_H_
/* Needed to detect STL */
#include <cstdlib>
// clang-format off
#ifndef __has_feature
#define __has_feature(x) 0
#endif
#if defined(__clang__)
#if !defined(COMPILER_CLANG)
#define COMPILER_CLANG
#endif
#elif defined(_MSC_VER)
#if !defined(COMPILER_MSVC)
#define COMPILER_MSVC
#endif
#elif defined(__GNUC__)
#if !defined(COMPILER_GCC)
#define COMPILER_GCC
#endif
#endif
#if __has_feature(cxx_attributes)
#define BENCHMARK_NORETURN [[noreturn]]
#elif defined(__GNUC__)
#define BENCHMARK_NORETURN __attribute__((noreturn))
#elif defined(COMPILER_MSVC)
#define BENCHMARK_NORETURN __declspec(noreturn)
#else
#define BENCHMARK_NORETURN
#endif
#if defined(__CYGWIN__)
#define BENCHMARK_OS_CYGWIN 1
#elif defined(_WIN32)
#define BENCHMARK_OS_WINDOWS 1
// WINAPI_FAMILY_PARTITION is defined in winapifamily.h.
// We include windows.h which implicitly includes winapifamily.h for compatibility.
#ifndef NOMINMAX
#define NOMINMAX
#endif
#include <windows.h>
#if defined(WINAPI_FAMILY_PARTITION)
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
#define BENCHMARK_OS_WINDOWS_WIN32 1
#elif WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP)
#define BENCHMARK_OS_WINDOWS_RT 1
#endif
#endif
#if defined(__MINGW32__)
#define BENCHMARK_OS_MINGW 1
#endif
#elif defined(__APPLE__)
#define BENCHMARK_OS_APPLE 1
#include "TargetConditionals.h"
#if defined(TARGET_OS_MAC)
#define BENCHMARK_OS_MACOSX 1
#if defined(TARGET_OS_IPHONE)
#define BENCHMARK_OS_IOS 1
#endif
#endif
#elif defined(__FreeBSD__)
#define BENCHMARK_OS_FREEBSD 1
#elif defined(__NetBSD__)
#define BENCHMARK_OS_NETBSD 1
#elif defined(__OpenBSD__)
#define BENCHMARK_OS_OPENBSD 1
#elif defined(__DragonFly__)
#define BENCHMARK_OS_DRAGONFLY 1
#elif defined(__linux__)
#define BENCHMARK_OS_LINUX 1
#elif defined(__native_client__)
#define BENCHMARK_OS_NACL 1
#elif defined(__EMSCRIPTEN__)
#define BENCHMARK_OS_EMSCRIPTEN 1
#elif defined(__rtems__)
#define BENCHMARK_OS_RTEMS 1
#elif defined(__Fuchsia__)
#define BENCHMARK_OS_FUCHSIA 1
#elif defined (__SVR4) && defined (__sun)
#define BENCHMARK_OS_SOLARIS 1
#elif defined(__QNX__)
#define BENCHMARK_OS_QNX 1
#elif defined(__MVS__)
#define BENCHMARK_OS_ZOS 1
#elif defined(__hexagon__)
#define BENCHMARK_OS_QURT 1
#endif
#if defined(__ANDROID__) && defined(__GLIBCXX__)
#define BENCHMARK_STL_ANDROID_GNUSTL 1
#endif
#if !__has_feature(cxx_exceptions) && !defined(__cpp_exceptions) \
&& !defined(__EXCEPTIONS)
#define BENCHMARK_HAS_NO_EXCEPTIONS
#endif
#if defined(COMPILER_CLANG) || defined(COMPILER_GCC)
#define BENCHMARK_MAYBE_UNUSED __attribute__((unused))
#else
#define BENCHMARK_MAYBE_UNUSED
#endif
// clang-format on
#endif // BENCHMARK_INTERNAL_MACROS_H_

View File

@@ -0,0 +1,341 @@
// Copyright 2015 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <algorithm>
#include <cmath>
#include <cstdint>
#include <iomanip> // for setprecision
#include <iostream>
#include <limits>
#include <string>
#include <tuple>
#include <vector>
#include "benchmark/benchmark.h"
#include "complexity.h"
#include "string_util.h"
#include "timers.h"
namespace benchmark {
namespace {
std::string StrEscape(const std::string& s) {
std::string tmp;
tmp.reserve(s.size());
for (char c : s) {
switch (c) {
case '\b':
tmp += "\\b";
break;
case '\f':
tmp += "\\f";
break;
case '\n':
tmp += "\\n";
break;
case '\r':
tmp += "\\r";
break;
case '\t':
tmp += "\\t";
break;
case '\\':
tmp += "\\\\";
break;
case '"':
tmp += "\\\"";
break;
default:
tmp += c;
break;
}
}
return tmp;
}
std::string FormatKV(std::string const& key, std::string const& value) {
return StrFormat("\"%s\": \"%s\"", StrEscape(key).c_str(),
StrEscape(value).c_str());
}
std::string FormatKV(std::string const& key, const char* value) {
return StrFormat("\"%s\": \"%s\"", StrEscape(key).c_str(),
StrEscape(value).c_str());
}
std::string FormatKV(std::string const& key, bool value) {
return StrFormat("\"%s\": %s", StrEscape(key).c_str(),
value ? "true" : "false");
}
std::string FormatKV(std::string const& key, int64_t value) {
std::stringstream ss;
ss << '"' << StrEscape(key) << "\": " << value;
return ss.str();
}
std::string FormatKV(std::string const& key, int value) {
return FormatKV(key, static_cast<int64_t>(value));
}
std::string FormatKV(std::string const& key, double value) {
std::stringstream ss;
ss << '"' << StrEscape(key) << "\": ";
if (std::isnan(value)) {
ss << (value < 0 ? "-" : "") << "NaN";
} else if (std::isinf(value)) {
ss << (value < 0 ? "-" : "") << "Infinity";
} else {
const auto max_digits10 =
std::numeric_limits<decltype(value)>::max_digits10;
const auto max_fractional_digits10 = max_digits10 - 1;
ss << std::scientific << std::setprecision(max_fractional_digits10)
<< value;
}
return ss.str();
}
int64_t RoundDouble(double v) { return std::lround(v); }
} // end namespace
bool JSONReporter::ReportContext(const Context& context) {
std::ostream& out = GetOutputStream();
out << "{\n";
std::string inner_indent(2, ' ');
// Open context block and print context information.
out << inner_indent << "\"context\": {\n";
std::string indent(4, ' ');
std::string walltime_value = LocalDateTimeString();
out << indent << FormatKV("date", walltime_value) << ",\n";
out << indent << FormatKV("host_name", context.sys_info.name) << ",\n";
if (Context::executable_name != nullptr) {
out << indent << FormatKV("executable", Context::executable_name) << ",\n";
}
CPUInfo const& info = context.cpu_info;
out << indent << FormatKV("num_cpus", static_cast<int64_t>(info.num_cpus))
<< ",\n";
out << indent
<< FormatKV("mhz_per_cpu",
RoundDouble(info.cycles_per_second / 1000000.0))
<< ",\n";
if (CPUInfo::Scaling::UNKNOWN != info.scaling) {
out << indent
<< FormatKV("cpu_scaling_enabled",
info.scaling == CPUInfo::Scaling::ENABLED)
<< ",\n";
}
const SystemInfo& sysinfo = context.sys_info;
if (SystemInfo::ASLR::UNKNOWN != sysinfo.ASLRStatus) {
out << indent
<< FormatKV("aslr_enabled",
sysinfo.ASLRStatus == SystemInfo::ASLR::ENABLED)
<< ",\n";
}
out << indent << "\"caches\": [\n";
indent = std::string(6, ' ');
std::string cache_indent(8, ' ');
for (size_t i = 0; i < info.caches.size(); ++i) {
const auto& CI = info.caches[i];
out << indent << "{\n";
out << cache_indent << FormatKV("type", CI.type) << ",\n";
out << cache_indent << FormatKV("level", static_cast<int64_t>(CI.level))
<< ",\n";
out << cache_indent << FormatKV("size", static_cast<int64_t>(CI.size))
<< ",\n";
out << cache_indent
<< FormatKV("num_sharing", static_cast<int64_t>(CI.num_sharing))
<< "\n";
out << indent << "}";
if (i != info.caches.size() - 1) {
out << ",";
}
out << "\n";
}
indent = std::string(4, ' ');
out << indent << "],\n";
out << indent << "\"load_avg\": [";
for (auto it = info.load_avg.begin(); it != info.load_avg.end();) {
out << *it++;
if (it != info.load_avg.end()) {
out << ",";
}
}
out << "],\n";
out << indent << FormatKV("library_version", GetBenchmarkVersion());
out << ",\n";
#if defined(NDEBUG)
const char build_type[] = "release";
#else
const char build_type[] = "debug";
#endif
out << indent << FormatKV("library_build_type", build_type);
out << ",\n";
// NOTE: our json schema is not strictly tied to the library version!
out << indent << FormatKV("json_schema_version", 1);
std::map<std::string, std::string>* global_context =
internal::GetGlobalContext();
if (global_context != nullptr) {
for (const auto& kv : *global_context) {
out << ",\n";
out << indent << FormatKV(kv.first, kv.second);
}
}
out << "\n";
// Close context block and open the list of benchmarks.
out << inner_indent << "},\n";
out << inner_indent << "\"benchmarks\": [\n";
return true;
}
void JSONReporter::ReportRuns(std::vector<Run> const& reports) {
if (reports.empty()) {
return;
}
std::string indent(4, ' ');
std::ostream& out = GetOutputStream();
if (!first_report_) {
out << ",\n";
}
first_report_ = false;
for (auto it = reports.begin(); it != reports.end(); ++it) {
out << indent << "{\n";
PrintRunData(*it);
out << indent << '}';
auto it_cp = it;
if (++it_cp != reports.end()) {
out << ",\n";
}
}
}
void JSONReporter::Finalize() {
// Close the list of benchmarks and the top level object.
GetOutputStream() << "\n ]\n}\n";
}
void JSONReporter::PrintRunData(Run const& run) {
std::string indent(6, ' ');
std::ostream& out = GetOutputStream();
out << indent << FormatKV("name", run.benchmark_name()) << ",\n";
out << indent << FormatKV("family_index", run.family_index) << ",\n";
out << indent
<< FormatKV("per_family_instance_index", run.per_family_instance_index)
<< ",\n";
out << indent << FormatKV("run_name", run.run_name.str()) << ",\n";
out << indent << FormatKV("run_type", [&run]() -> const char* {
switch (run.run_type) {
case BenchmarkReporter::Run::RT_Iteration:
return "iteration";
case BenchmarkReporter::Run::RT_Aggregate:
return "aggregate";
}
BENCHMARK_UNREACHABLE();
}()) << ",\n";
out << indent << FormatKV("repetitions", run.repetitions) << ",\n";
if (run.run_type != BenchmarkReporter::Run::RT_Aggregate) {
out << indent << FormatKV("repetition_index", run.repetition_index)
<< ",\n";
}
out << indent << FormatKV("threads", run.threads) << ",\n";
if (run.run_type == BenchmarkReporter::Run::RT_Aggregate) {
out << indent << FormatKV("aggregate_name", run.aggregate_name) << ",\n";
out << indent << FormatKV("aggregate_unit", [&run]() -> const char* {
switch (run.aggregate_unit) {
case StatisticUnit::kTime:
return "time";
case StatisticUnit::kPercentage:
return "percentage";
}
BENCHMARK_UNREACHABLE();
}()) << ",\n";
}
if (internal::SkippedWithError == run.skipped) {
out << indent << FormatKV("error_occurred", true) << ",\n";
out << indent << FormatKV("error_message", run.skip_message) << ",\n";
} else if (internal::SkippedWithMessage == run.skipped) {
out << indent << FormatKV("skipped", true) << ",\n";
out << indent << FormatKV("skip_message", run.skip_message) << ",\n";
}
if (!run.report_big_o && !run.report_rms) {
out << indent << FormatKV("iterations", run.iterations) << ",\n";
if (run.run_type != Run::RT_Aggregate ||
run.aggregate_unit == StatisticUnit::kTime) {
out << indent << FormatKV("real_time", run.GetAdjustedRealTime())
<< ",\n";
out << indent << FormatKV("cpu_time", run.GetAdjustedCPUTime());
} else {
assert(run.aggregate_unit == StatisticUnit::kPercentage);
out << indent << FormatKV("real_time", run.real_accumulated_time)
<< ",\n";
out << indent << FormatKV("cpu_time", run.cpu_accumulated_time);
}
out << ",\n"
<< indent << FormatKV("time_unit", GetTimeUnitString(run.time_unit));
} else if (run.report_big_o) {
out << indent << FormatKV("cpu_coefficient", run.GetAdjustedCPUTime())
<< ",\n";
out << indent << FormatKV("real_coefficient", run.GetAdjustedRealTime())
<< ",\n";
out << indent << FormatKV("big_o", GetBigOString(run.complexity)) << ",\n";
out << indent << FormatKV("time_unit", GetTimeUnitString(run.time_unit));
} else if (run.report_rms) {
out << indent << FormatKV("rms", run.GetAdjustedCPUTime());
}
for (const auto& c : run.counters) {
out << ",\n" << indent << FormatKV(c.first, c.second);
}
if (run.memory_result.memory_iterations > 0) {
const auto& memory_result = run.memory_result;
out << ",\n" << indent << FormatKV("allocs_per_iter", run.allocs_per_iter);
out << ",\n"
<< indent << FormatKV("max_bytes_used", memory_result.max_bytes_used);
auto report_if_present = [&out, &indent](const std::string& label,
int64_t val) {
if (val != MemoryManager::TombstoneValue) {
out << ",\n" << indent << FormatKV(label, val);
}
};
report_if_present("total_allocated_bytes",
memory_result.total_allocated_bytes);
report_if_present("net_heap_growth", memory_result.net_heap_growth);
}
if (!run.report_label.empty()) {
out << ",\n" << indent << FormatKV("label", run.report_label);
}
out << '\n';
}
} // end namespace benchmark

View File

@@ -0,0 +1,76 @@
#ifndef BENCHMARK_LOG_H_
#define BENCHMARK_LOG_H_
#include <iostream>
#include <ostream>
namespace benchmark {
namespace internal {
typedef std::basic_ostream<char>&(EndLType)(std::basic_ostream<char>&);
class LogType {
friend LogType& GetNullLogInstance();
friend LogType& GetErrorLogInstance();
// FIXME: Add locking to output.
template <class Tp>
friend LogType& operator<<(LogType&, Tp const&);
friend LogType& operator<<(LogType&, EndLType*);
private:
LogType(std::ostream* out) : out_(out) {}
std::ostream* out_;
// NOTE: we could use BENCHMARK_DISALLOW_COPY_AND_ASSIGN but we shouldn't have
// a dependency on benchmark.h from here.
LogType(const LogType&) = delete;
LogType& operator=(const LogType&) = delete;
};
template <class Tp>
LogType& operator<<(LogType& log, Tp const& value) {
if (log.out_) {
*log.out_ << value;
}
return log;
}
inline LogType& operator<<(LogType& log, EndLType* m) {
if (log.out_) {
*log.out_ << m;
}
return log;
}
inline int& LogLevel() {
static int log_level = 0;
return log_level;
}
inline LogType& GetNullLogInstance() {
static LogType null_log(static_cast<std::ostream*>(nullptr));
return null_log;
}
inline LogType& GetErrorLogInstance() {
static LogType error_log(&std::clog);
return error_log;
}
inline LogType& GetLogInstanceForLevel(int level) {
if (level <= LogLevel()) {
return GetErrorLogInstance();
}
return GetNullLogInstance();
}
} // end namespace internal
} // end namespace benchmark
// clang-format off
#define BM_VLOG(x) \
(::benchmark::internal::GetLogInstanceForLevel(x) << "-- LOG(" << x << "):" \
" ")
// clang-format on
#endif

View File

@@ -0,0 +1,155 @@
#ifndef BENCHMARK_MUTEX_H_
#define BENCHMARK_MUTEX_H_
#include <condition_variable>
#include <mutex>
#include "check.h"
// Enable thread safety attributes only with clang.
// The attributes can be safely erased when compiling with other compilers.
#if defined(HAVE_THREAD_SAFETY_ATTRIBUTES)
#define THREAD_ANNOTATION_ATTRIBUTE_(x) __attribute__((x))
#else
#define THREAD_ANNOTATION_ATTRIBUTE_(x) // no-op
#endif
#define CAPABILITY(x) THREAD_ANNOTATION_ATTRIBUTE_(capability(x))
#define SCOPED_CAPABILITY THREAD_ANNOTATION_ATTRIBUTE_(scoped_lockable)
#define GUARDED_BY(x) THREAD_ANNOTATION_ATTRIBUTE_(guarded_by(x))
#define PT_GUARDED_BY(x) THREAD_ANNOTATION_ATTRIBUTE_(pt_guarded_by(x))
#define ACQUIRED_BEFORE(...) \
THREAD_ANNOTATION_ATTRIBUTE_(acquired_before(__VA_ARGS__))
#define ACQUIRED_AFTER(...) \
THREAD_ANNOTATION_ATTRIBUTE_(acquired_after(__VA_ARGS__))
#define REQUIRES(...) \
THREAD_ANNOTATION_ATTRIBUTE_(requires_capability(__VA_ARGS__))
#define REQUIRES_SHARED(...) \
THREAD_ANNOTATION_ATTRIBUTE_(requires_shared_capability(__VA_ARGS__))
#define ACQUIRE(...) \
THREAD_ANNOTATION_ATTRIBUTE_(acquire_capability(__VA_ARGS__))
#define ACQUIRE_SHARED(...) \
THREAD_ANNOTATION_ATTRIBUTE_(acquire_shared_capability(__VA_ARGS__))
#define RELEASE(...) \
THREAD_ANNOTATION_ATTRIBUTE_(release_capability(__VA_ARGS__))
#define RELEASE_SHARED(...) \
THREAD_ANNOTATION_ATTRIBUTE_(release_shared_capability(__VA_ARGS__))
#define TRY_ACQUIRE(...) \
THREAD_ANNOTATION_ATTRIBUTE_(try_acquire_capability(__VA_ARGS__))
#define TRY_ACQUIRE_SHARED(...) \
THREAD_ANNOTATION_ATTRIBUTE_(try_acquire_shared_capability(__VA_ARGS__))
#define EXCLUDES(...) THREAD_ANNOTATION_ATTRIBUTE_(locks_excluded(__VA_ARGS__))
#define ASSERT_CAPABILITY(x) THREAD_ANNOTATION_ATTRIBUTE_(assert_capability(x))
#define ASSERT_SHARED_CAPABILITY(x) \
THREAD_ANNOTATION_ATTRIBUTE_(assert_shared_capability(x))
#define RETURN_CAPABILITY(x) THREAD_ANNOTATION_ATTRIBUTE_(lock_returned(x))
#define NO_THREAD_SAFETY_ANALYSIS \
THREAD_ANNOTATION_ATTRIBUTE_(no_thread_safety_analysis)
namespace benchmark {
typedef std::condition_variable Condition;
// NOTE: Wrappers for std::mutex and std::unique_lock are provided so that
// we can annotate them with thread safety attributes and use the
// -Wthread-safety warning with clang. The standard library types cannot be
// used directly because they do not provide the required annotations.
class CAPABILITY("mutex") Mutex {
public:
Mutex() {}
void lock() ACQUIRE() { mut_.lock(); }
void unlock() RELEASE() { mut_.unlock(); }
std::mutex& native_handle() { return mut_; }
private:
std::mutex mut_;
};
class SCOPED_CAPABILITY MutexLock {
typedef std::unique_lock<std::mutex> MutexLockImp;
public:
MutexLock(Mutex& m) ACQUIRE(m) : ml_(m.native_handle()) {}
~MutexLock() RELEASE() {}
MutexLockImp& native_handle() { return ml_; }
private:
MutexLockImp ml_;
};
class Barrier {
public:
Barrier(int num_threads) : running_threads_(num_threads) {}
// Called by each thread
bool wait() EXCLUDES(lock_) {
bool last_thread = false;
{
MutexLock ml(lock_);
last_thread = createBarrier(ml);
}
if (last_thread) phase_condition_.notify_all();
return last_thread;
}
void removeThread() EXCLUDES(lock_) {
MutexLock ml(lock_);
--running_threads_;
if (entered_ != 0) phase_condition_.notify_all();
}
private:
Mutex lock_;
Condition phase_condition_;
int running_threads_;
// State for barrier management
int phase_number_ = 0;
int entered_ = 0; // Number of threads that have entered this barrier
// Enter the barrier and wait until all other threads have also
// entered the barrier. Returns iff this is the last thread to
// enter the barrier.
bool createBarrier(MutexLock& ml) REQUIRES(lock_) {
BM_CHECK_LT(entered_, running_threads_);
entered_++;
if (entered_ < running_threads_) {
// Wait for all threads to enter
int phase_number_cp = phase_number_;
auto cb = [this, phase_number_cp]() {
return this->phase_number_ > phase_number_cp ||
entered_ == running_threads_; // A thread has aborted in error
};
phase_condition_.wait(ml.native_handle(), cb);
if (phase_number_ > phase_number_cp) return false;
// else (running_threads_ == entered_) and we are the last thread.
}
// Last thread has reached the barrier
phase_number_++;
entered_ = 0;
return true;
}
};
} // end namespace benchmark
#endif // BENCHMARK_MUTEX_H_

View File

@@ -0,0 +1,282 @@
// Copyright 2021 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "perf_counters.h"
#include <cstring>
#include <memory>
#include <vector>
#if defined HAVE_LIBPFM
#include "perfmon/pfmlib.h"
#include "perfmon/pfmlib_perf_event.h"
#endif
namespace benchmark {
namespace internal {
#if defined HAVE_LIBPFM
size_t PerfCounterValues::Read(const std::vector<int>& leaders) {
// Create a pointer for multiple reads
const size_t bufsize = values_.size() * sizeof(values_[0]);
char* ptr = reinterpret_cast<char*>(values_.data());
size_t size = bufsize;
for (int lead : leaders) {
auto read_bytes = ::read(lead, ptr, size);
if (read_bytes >= ssize_t(sizeof(uint64_t))) {
// Actual data bytes are all bytes minus initial padding
std::size_t data_bytes =
static_cast<std::size_t>(read_bytes) - sizeof(uint64_t);
// This should be very cheap since it's in hot cache
std::memmove(ptr, ptr + sizeof(uint64_t), data_bytes);
// Increment our counters
ptr += data_bytes;
size -= data_bytes;
} else {
int err = errno;
GetErrorLogInstance() << "Error reading lead " << lead << " errno:" << err
<< " " << ::strerror(err) << "\n";
return 0;
}
}
return (bufsize - size) / sizeof(uint64_t);
}
const bool PerfCounters::kSupported = true;
// Initializes libpfm only on the first call. Returns whether that single
// initialization was successful.
bool PerfCounters::Initialize() {
// Function-scope static gets initialized only once on first call.
static const bool success = []() {
return pfm_initialize() == PFM_SUCCESS;
}();
return success;
}
bool PerfCounters::IsCounterSupported(const std::string& name) {
Initialize();
perf_event_attr_t attr;
std::memset(&attr, 0, sizeof(attr));
pfm_perf_encode_arg_t arg;
std::memset(&arg, 0, sizeof(arg));
arg.attr = &attr;
const int mode = PFM_PLM3; // user mode only
int ret = pfm_get_os_event_encoding(name.c_str(), mode, PFM_OS_PERF_EVENT_EXT,
&arg);
return (ret == PFM_SUCCESS);
}
PerfCounters PerfCounters::Create(
const std::vector<std::string>& counter_names) {
if (!counter_names.empty()) {
Initialize();
}
// Valid counters will populate these arrays but we start empty
std::vector<std::string> valid_names;
std::vector<int> counter_ids;
std::vector<int> leader_ids;
// Resize to the maximum possible
valid_names.reserve(counter_names.size());
counter_ids.reserve(counter_names.size());
const int kCounterMode = PFM_PLM3; // user mode only
// Group leads will be assigned on demand. The idea is that once we cannot
// create a counter descriptor, the reason is that this group has maxed out
// so we set the group_id again to -1 and retry - giving the algorithm a
// chance to create a new group leader to hold the next set of counters.
int group_id = -1;
// Loop through all performance counters
for (size_t i = 0; i < counter_names.size(); ++i) {
// we are about to push into the valid names vector
// check if we did not reach the maximum
if (valid_names.size() == PerfCounterValues::kMaxCounters) {
// Log a message if we maxed out and stop adding
GetErrorLogInstance()
<< counter_names.size() << " counters were requested. The maximum is "
<< PerfCounterValues::kMaxCounters << " and " << valid_names.size()
<< " were already added. All remaining counters will be ignored\n";
// stop the loop and return what we have already
break;
}
// Check if this name is empty
const auto& name = counter_names[i];
if (name.empty()) {
GetErrorLogInstance()
<< "A performance counter name was the empty string\n";
continue;
}
// Here first means first in group, ie the group leader
const bool is_first = (group_id < 0);
// This struct will be populated by libpfm from the counter string
// and then fed into the syscall perf_event_open
struct perf_event_attr attr {};
attr.size = sizeof(attr);
// This is the input struct to libpfm.
pfm_perf_encode_arg_t arg{};
arg.attr = &attr;
const int pfm_get = pfm_get_os_event_encoding(name.c_str(), kCounterMode,
PFM_OS_PERF_EVENT, &arg);
if (pfm_get != PFM_SUCCESS) {
GetErrorLogInstance()
<< "Unknown performance counter name: " << name << "\n";
continue;
}
// We then proceed to populate the remaining fields in our attribute struct
// Note: the man page for perf_event_create suggests inherit = true and
// read_format = PERF_FORMAT_GROUP don't work together, but that's not the
// case.
attr.disabled = is_first;
attr.inherit = true;
attr.pinned = is_first;
attr.exclude_kernel = true;
attr.exclude_user = false;
attr.exclude_hv = true;
// Read all counters in a group in one read.
attr.read_format = PERF_FORMAT_GROUP; //| PERF_FORMAT_TOTAL_TIME_ENABLED |
// PERF_FORMAT_TOTAL_TIME_RUNNING;
int id = -1;
while (id < 0) {
static constexpr size_t kNrOfSyscallRetries = 5;
// Retry syscall as it was interrupted often (b/64774091).
for (size_t num_retries = 0; num_retries < kNrOfSyscallRetries;
++num_retries) {
id = perf_event_open(&attr, 0, -1, group_id, 0);
if (id >= 0 || errno != EINTR) {
break;
}
}
if (id < 0) {
// If the file descriptor is negative we might have reached a limit
// in the current group. Set the group_id to -1 and retry
if (group_id >= 0) {
// Create a new group
group_id = -1;
} else {
// At this point we have already retried to set a new group id and
// failed. We then give up.
break;
}
}
}
// We failed to get a new file descriptor. We might have reached a hard
// hardware limit that cannot be resolved even with group multiplexing
if (id < 0) {
GetErrorLogInstance() << "***WARNING** Failed to get a file descriptor "
"for performance counter "
<< name << ". Ignoring\n";
// We give up on this counter but try to keep going
// as the others would be fine
continue;
}
if (group_id < 0) {
// This is a leader, store and assign it to the current file descriptor
leader_ids.push_back(id);
group_id = id;
}
// This is a valid counter, add it to our descriptor's list
counter_ids.push_back(id);
valid_names.push_back(name);
}
// Loop through all group leaders activating them
// There is another option of starting ALL counters in a process but
// that would be far reaching an intrusion. If the user is using PMCs
// by themselves then this would have a side effect on them. It is
// friendlier to loop through all groups individually.
for (int lead : leader_ids) {
if (ioctl(lead, PERF_EVENT_IOC_ENABLE) != 0) {
// This should never happen but if it does, we give up on the
// entire batch as recovery would be a mess.
GetErrorLogInstance() << "***WARNING*** Failed to start counters. "
"Claring out all counters.\n";
// Close all performance counters
for (int id : counter_ids) {
::close(id);
}
// Return an empty object so our internal state is still good and
// the process can continue normally without impact
return NoCounters();
}
}
return PerfCounters(std::move(valid_names), std::move(counter_ids),
std::move(leader_ids));
}
void PerfCounters::CloseCounters() const {
if (counter_ids_.empty()) {
return;
}
for (int lead : leader_ids_) {
ioctl(lead, PERF_EVENT_IOC_DISABLE);
}
for (int fd : counter_ids_) {
close(fd);
}
}
#else // defined HAVE_LIBPFM
size_t PerfCounterValues::Read(const std::vector<int>&) { return 0; }
const bool PerfCounters::kSupported = false;
bool PerfCounters::Initialize() { return false; }
bool PerfCounters::IsCounterSupported(const std::string&) { return false; }
PerfCounters PerfCounters::Create(
const std::vector<std::string>& counter_names) {
if (!counter_names.empty()) {
GetErrorLogInstance() << "Performance counters not supported.\n";
}
return NoCounters();
}
void PerfCounters::CloseCounters() const {}
#endif // defined HAVE_LIBPFM
PerfCountersMeasurement::PerfCountersMeasurement(
const std::vector<std::string>& counter_names)
: start_values_(counter_names.size()), end_values_(counter_names.size()) {
counters_ = PerfCounters::Create(counter_names);
}
PerfCounters& PerfCounters::operator=(PerfCounters&& other) noexcept {
if (this != &other) {
CloseCounters();
counter_ids_ = std::move(other.counter_ids_);
leader_ids_ = std::move(other.leader_ids_);
counter_names_ = std::move(other.counter_names_);
}
return *this;
}
} // namespace internal
} // namespace benchmark

View File

@@ -0,0 +1,200 @@
// Copyright 2021 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef BENCHMARK_PERF_COUNTERS_H
#define BENCHMARK_PERF_COUNTERS_H
#include <array>
#include <cstdint>
#include <cstring>
#include <memory>
#include <vector>
#include "benchmark/benchmark.h"
#include "check.h"
#include "log.h"
#include "mutex.h"
#ifndef BENCHMARK_OS_WINDOWS
#include <unistd.h>
#endif
#if defined(_MSC_VER)
#pragma warning(push)
// C4251: <symbol> needs to have dll-interface to be used by clients of class
#pragma warning(disable : 4251)
#endif
namespace benchmark {
namespace internal {
// Typically, we can only read a small number of counters. There is also a
// padding preceding counter values, when reading multiple counters with one
// syscall (which is desirable). PerfCounterValues abstracts these details.
// The implementation ensures the storage is inlined, and allows 0-based
// indexing into the counter values.
// The object is used in conjunction with a PerfCounters object, by passing it
// to Snapshot(). The Read() method relocates individual reads, discarding
// the initial padding from each group leader in the values buffer such that
// all user accesses through the [] operator are correct.
class BENCHMARK_EXPORT PerfCounterValues {
public:
explicit PerfCounterValues(size_t nr_counters) : nr_counters_(nr_counters) {
BM_CHECK_LE(nr_counters_, kMaxCounters);
}
// We are reading correctly now so the values don't need to skip padding
uint64_t operator[](size_t pos) const { return values_[pos]; }
// Increased the maximum to 32 only since the buffer
// is std::array<> backed
static constexpr size_t kMaxCounters = 32;
private:
friend class PerfCounters;
// Get the byte buffer in which perf counters can be captured.
// This is used by PerfCounters::Read
std::pair<char*, size_t> get_data_buffer() {
return {reinterpret_cast<char*>(values_.data()),
sizeof(uint64_t) * (kPadding + nr_counters_)};
}
// This reading is complex and as the goal of this class is to
// abstract away the intrincacies of the reading process, this is
// a better place for it
size_t Read(const std::vector<int>& leaders);
// Move the padding to 2 due to the reading algorithm (1st padding plus a
// current read padding)
static constexpr size_t kPadding = 2;
std::array<uint64_t, kPadding + kMaxCounters> values_;
const size_t nr_counters_;
};
// Collect PMU counters. The object, once constructed, is ready to be used by
// calling read(). PMU counter collection is enabled from the time create() is
// called, to obtain the object, until the object's destructor is called.
class BENCHMARK_EXPORT PerfCounters final {
public:
// True iff this platform supports performance counters.
static const bool kSupported;
// Returns an empty object
static PerfCounters NoCounters() { return PerfCounters(); }
~PerfCounters() { CloseCounters(); }
PerfCounters() = default;
PerfCounters(PerfCounters&&) = default;
PerfCounters(const PerfCounters&) = delete;
PerfCounters& operator=(PerfCounters&&) noexcept;
PerfCounters& operator=(const PerfCounters&) = delete;
// Platform-specific implementations may choose to do some library
// initialization here.
static bool Initialize();
// Check if the given counter is supported, if the app wants to
// check before passing
static bool IsCounterSupported(const std::string& name);
// Return a PerfCounters object ready to read the counters with the names
// specified. The values are user-mode only. The counter name format is
// implementation and OS specific.
// In case of failure, this method will in the worst case return an
// empty object whose state will still be valid.
static PerfCounters Create(const std::vector<std::string>& counter_names);
// Take a snapshot of the current value of the counters into the provided
// valid PerfCounterValues storage. The values are populated such that:
// names()[i]'s value is (*values)[i]
BENCHMARK_ALWAYS_INLINE bool Snapshot(PerfCounterValues* values) const {
#ifndef BENCHMARK_OS_WINDOWS
assert(values != nullptr);
return values->Read(leader_ids_) == counter_ids_.size();
#else
(void)values;
return false;
#endif
}
const std::vector<std::string>& names() const { return counter_names_; }
size_t num_counters() const { return counter_names_.size(); }
private:
PerfCounters(const std::vector<std::string>& counter_names,
std::vector<int>&& counter_ids, std::vector<int>&& leader_ids)
: counter_ids_(std::move(counter_ids)),
leader_ids_(std::move(leader_ids)),
counter_names_(counter_names) {}
void CloseCounters() const;
std::vector<int> counter_ids_;
std::vector<int> leader_ids_;
std::vector<std::string> counter_names_;
};
// Typical usage of the above primitives.
class BENCHMARK_EXPORT PerfCountersMeasurement final {
public:
PerfCountersMeasurement(const std::vector<std::string>& counter_names);
size_t num_counters() const { return counters_.num_counters(); }
std::vector<std::string> names() const { return counters_.names(); }
BENCHMARK_ALWAYS_INLINE bool Start() {
if (num_counters() == 0) return true;
// Tell the compiler to not move instructions above/below where we take
// the snapshot.
ClobberMemory();
valid_read_ &= counters_.Snapshot(&start_values_);
ClobberMemory();
return valid_read_;
}
BENCHMARK_ALWAYS_INLINE bool Stop(
std::vector<std::pair<std::string, double>>& measurements) {
if (num_counters() == 0) return true;
// Tell the compiler to not move instructions above/below where we take
// the snapshot.
ClobberMemory();
valid_read_ &= counters_.Snapshot(&end_values_);
ClobberMemory();
for (size_t i = 0; i < counters_.names().size(); ++i) {
double measurement = static_cast<double>(end_values_[i]) -
static_cast<double>(start_values_[i]);
measurements.push_back({counters_.names()[i], measurement});
}
return valid_read_;
}
private:
PerfCounters counters_;
bool valid_read_ = true;
PerfCounterValues start_values_;
PerfCounterValues end_values_;
};
} // namespace internal
} // namespace benchmark
#if defined(_MSC_VER)
#pragma warning(pop)
#endif
#endif // BENCHMARK_PERF_COUNTERS_H

View File

@@ -0,0 +1,156 @@
// Copyright 2015 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef BENCHMARK_RE_H_
#define BENCHMARK_RE_H_
#include "internal_macros.h"
// clang-format off
#if !defined(HAVE_STD_REGEX) && \
!defined(HAVE_GNU_POSIX_REGEX) && \
!defined(HAVE_POSIX_REGEX)
// No explicit regex selection; detect based on builtin hints.
#if defined(BENCHMARK_OS_LINUX) || defined(BENCHMARK_OS_APPLE)
#define HAVE_POSIX_REGEX 1
#elif __cplusplus >= 199711L
#define HAVE_STD_REGEX 1
#endif
#endif
// Prefer C regex libraries when compiling w/o exceptions so that we can
// correctly report errors.
#if defined(BENCHMARK_HAS_NO_EXCEPTIONS) && \
defined(HAVE_STD_REGEX) && \
(defined(HAVE_GNU_POSIX_REGEX) || defined(HAVE_POSIX_REGEX))
#undef HAVE_STD_REGEX
#endif
#if defined(HAVE_STD_REGEX)
#include <regex>
#elif defined(HAVE_GNU_POSIX_REGEX)
#include <gnuregex.h>
#elif defined(HAVE_POSIX_REGEX)
#include <regex.h>
#else
#error No regular expression backend was found!
#endif
// clang-format on
#include <string>
#include "check.h"
namespace benchmark {
// A wrapper around the POSIX regular expression API that provides automatic
// cleanup
class Regex {
public:
Regex() : init_(false) {}
~Regex();
// Compile a regular expression matcher from spec. Returns true on success.
//
// On failure (and if error is not nullptr), error is populated with a human
// readable error message if an error occurs.
bool Init(const std::string& spec, std::string* error);
// Returns whether str matches the compiled regular expression.
bool Match(const std::string& str);
private:
bool init_;
// Underlying regular expression object
#if defined(HAVE_STD_REGEX)
std::regex re_;
#elif defined(HAVE_POSIX_REGEX) || defined(HAVE_GNU_POSIX_REGEX)
regex_t re_;
#else
#error No regular expression backend implementation available
#endif
};
#if defined(HAVE_STD_REGEX)
inline bool Regex::Init(const std::string& spec, std::string* error) {
#ifdef BENCHMARK_HAS_NO_EXCEPTIONS
((void)error); // suppress unused warning
#else
try {
#endif
re_ = std::regex(spec, std::regex_constants::extended);
init_ = true;
#ifndef BENCHMARK_HAS_NO_EXCEPTIONS
}
catch (const std::regex_error& e) {
if (error) {
*error = e.what();
}
}
#endif
return init_;
}
inline Regex::~Regex() {}
inline bool Regex::Match(const std::string& str) {
if (!init_) {
return false;
}
return std::regex_search(str, re_);
}
#else
inline bool Regex::Init(const std::string& spec, std::string* error) {
int ec = regcomp(&re_, spec.c_str(), REG_EXTENDED | REG_NOSUB);
if (ec != 0) {
if (error) {
size_t needed = regerror(ec, &re_, nullptr, 0);
std::vector<char> errbuf(needed);
regerror(ec, &re_, errbuf.data(), needed);
// regerror returns the number of bytes necessary to null terminate
// the string, so we move that when assigning to error.
BM_CHECK_NE(needed, 0);
error->assign(errbuf.data(), needed - 1);
}
return false;
}
init_ = true;
return true;
}
inline Regex::~Regex() {
if (init_) {
regfree(&re_);
}
}
inline bool Regex::Match(const std::string& str) {
if (!init_) {
return false;
}
return regexec(&re_, str.c_str(), 0, nullptr, 0) == 0;
}
#endif
} // end namespace benchmark
#endif // BENCHMARK_RE_H_

View File

@@ -0,0 +1,133 @@
// Copyright 2015 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <cstdlib>
#include <iostream>
#include <map>
#include <string>
#include <tuple>
#include <vector>
#include "benchmark/benchmark.h"
#include "check.h"
#include "string_util.h"
#include "timers.h"
namespace benchmark {
BenchmarkReporter::BenchmarkReporter()
: output_stream_(&std::cout), error_stream_(&std::cerr) {}
BenchmarkReporter::~BenchmarkReporter() {}
void BenchmarkReporter::PrintBasicContext(std::ostream *out,
Context const &context) {
BM_CHECK(out) << "cannot be null";
auto &Out = *out;
#ifndef BENCHMARK_OS_QURT
// Date/time information is not available on QuRT.
// Attempting to get it via this call cause the binary to crash.
Out << LocalDateTimeString() << "\n";
#endif
if (benchmark::BenchmarkReporter::Context::executable_name != nullptr) {
Out << "Running " << benchmark::BenchmarkReporter::Context::executable_name
<< "\n";
}
const CPUInfo &info = context.cpu_info;
Out << "Run on (" << info.num_cpus << " X "
<< (info.cycles_per_second / 1000000.0) << " MHz CPU "
<< ((info.num_cpus > 1) ? "s" : "") << ")\n";
if (!info.caches.empty()) {
Out << "CPU Caches:\n";
for (const auto &CInfo : info.caches) {
Out << " L" << CInfo.level << " " << CInfo.type << " "
<< (CInfo.size / 1024) << " KiB";
if (CInfo.num_sharing != 0) {
Out << " (x" << (info.num_cpus / CInfo.num_sharing) << ")";
}
Out << "\n";
}
}
if (!info.load_avg.empty()) {
Out << "Load Average: ";
for (auto It = info.load_avg.begin(); It != info.load_avg.end();) {
Out << StrFormat("%.2f", *It++);
if (It != info.load_avg.end()) {
Out << ", ";
}
}
Out << "\n";
}
std::map<std::string, std::string> *global_context =
internal::GetGlobalContext();
if (global_context != nullptr) {
for (const auto &kv : *global_context) {
Out << kv.first << ": " << kv.second << "\n";
}
}
if (CPUInfo::Scaling::ENABLED == info.scaling) {
Out << "***WARNING*** CPU scaling is enabled, the benchmark "
"real time measurements may be noisy and will incur extra "
"overhead.\n";
}
const SystemInfo &sysinfo = context.sys_info;
if (SystemInfo::ASLR::ENABLED == sysinfo.ASLRStatus) {
Out << "***WARNING*** ASLR is enabled, the results may have unreproducible "
"noise in them.\n";
}
#ifndef NDEBUG
Out << "***WARNING*** Library was built as DEBUG. Timings may be "
"affected.\n";
#endif
}
// No initializer because it's already initialized to NULL.
const char *BenchmarkReporter::Context::executable_name;
BenchmarkReporter::Context::Context()
: cpu_info(CPUInfo::Get()), sys_info(SystemInfo::Get()) {}
std::string BenchmarkReporter::Run::benchmark_name() const {
std::string name = run_name.str();
if (run_type == RT_Aggregate) {
name += "_" + aggregate_name;
}
return name;
}
double BenchmarkReporter::Run::GetAdjustedRealTime() const {
double new_time = real_accumulated_time * GetTimeUnitMultiplier(time_unit);
if (iterations != 0) {
new_time /= static_cast<double>(iterations);
}
return new_time;
}
double BenchmarkReporter::Run::GetAdjustedCPUTime() const {
double new_time = cpu_accumulated_time * GetTimeUnitMultiplier(time_unit);
if (iterations != 0) {
new_time /= static_cast<double>(iterations);
}
return new_time;
}
} // end namespace benchmark

View File

@@ -0,0 +1,232 @@
// Copyright 2016 Ismael Jimenez Martinez. All rights reserved.
// Copyright 2017 Roman Lebedev. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "statistics.h"
#include <algorithm>
#include <cmath>
#include <numeric>
#include <string>
#include <vector>
#include "benchmark/benchmark.h"
#include "check.h"
namespace benchmark {
const auto StatisticsSum = [](const std::vector<double>& v) {
return std::accumulate(v.begin(), v.end(), 0.0);
};
double StatisticsMean(const std::vector<double>& v) {
if (v.empty()) {
return 0.0;
}
return StatisticsSum(v) * (1.0 / static_cast<double>(v.size()));
}
double StatisticsMedian(const std::vector<double>& v) {
if (v.size() < 3) {
return StatisticsMean(v);
}
std::vector<double> copy(v);
auto center = copy.begin() + v.size() / 2;
std::nth_element(copy.begin(), center, copy.end());
// Did we have an odd number of samples? If yes, then center is the median.
// If not, then we are looking for the average between center and the value
// before. Instead of resorting, we just look for the max value before it,
// which is not necessarily the element immediately preceding `center` Since
// `copy` is only partially sorted by `nth_element`.
if (v.size() % 2 == 1) {
return *center;
}
auto center2 = std::max_element(copy.begin(), center);
return (*center + *center2) / 2.0;
}
// Return the sum of the squares of this sample set
const auto SumSquares = [](const std::vector<double>& v) {
return std::inner_product(v.begin(), v.end(), v.begin(), 0.0);
};
const auto Sqr = [](const double dat) { return dat * dat; };
const auto Sqrt = [](const double dat) {
// Avoid NaN due to imprecision in the calculations
if (dat < 0.0) {
return 0.0;
}
return std::sqrt(dat);
};
double StatisticsStdDev(const std::vector<double>& v) {
const auto mean = StatisticsMean(v);
if (v.empty()) {
return mean;
}
// Sample standard deviation is undefined for n = 1
if (v.size() == 1) {
return 0.0;
}
const double avg_squares =
SumSquares(v) * (1.0 / static_cast<double>(v.size()));
return Sqrt(static_cast<double>(v.size()) /
(static_cast<double>(v.size()) - 1.0) *
(avg_squares - Sqr(mean)));
}
double StatisticsCV(const std::vector<double>& v) {
if (v.size() < 2) {
return 0.0;
}
const auto stddev = StatisticsStdDev(v);
const auto mean = StatisticsMean(v);
if (std::fpclassify(mean) == FP_ZERO) {
return 0.0;
}
return stddev / mean;
}
std::vector<BenchmarkReporter::Run> ComputeStats(
const std::vector<BenchmarkReporter::Run>& reports) {
typedef BenchmarkReporter::Run Run;
std::vector<Run> results;
auto error_count = std::count_if(reports.begin(), reports.end(),
[](Run const& run) { return run.skipped; });
if (reports.size() - static_cast<size_t>(error_count) < 2) {
// We don't report aggregated data if there was a single run.
return results;
}
// Accumulators.
std::vector<double> real_accumulated_time_stat;
std::vector<double> cpu_accumulated_time_stat;
real_accumulated_time_stat.reserve(reports.size());
cpu_accumulated_time_stat.reserve(reports.size());
// All repetitions should be run with the same number of iterations so we
// can take this information from the first benchmark.
const IterationCount run_iterations = reports.front().iterations;
// create stats for user counters
struct CounterStat {
Counter c;
std::vector<double> s;
};
std::map<std::string, CounterStat> counter_stats;
for (Run const& r : reports) {
for (auto const& cnt : r.counters) {
auto it = counter_stats.find(cnt.first);
if (it == counter_stats.end()) {
it = counter_stats
.emplace(cnt.first,
CounterStat{cnt.second, std::vector<double>{}})
.first;
it->second.s.reserve(reports.size());
} else {
BM_CHECK_EQ(it->second.c.flags, cnt.second.flags);
}
}
}
// Populate the accumulators.
for (Run const& run : reports) {
BM_CHECK_EQ(reports[0].benchmark_name(), run.benchmark_name());
BM_CHECK_EQ(run_iterations, run.iterations);
if (run.skipped != 0u) {
continue;
}
real_accumulated_time_stat.emplace_back(run.real_accumulated_time);
cpu_accumulated_time_stat.emplace_back(run.cpu_accumulated_time);
// user counters
for (auto const& cnt : run.counters) {
auto it = counter_stats.find(cnt.first);
BM_CHECK_NE(it, counter_stats.end());
it->second.s.emplace_back(cnt.second);
}
}
// Only add label if it is same for all runs
std::string report_label = reports[0].report_label;
for (std::size_t i = 1; i < reports.size(); i++) {
if (reports[i].report_label != report_label) {
report_label = "";
break;
}
}
const double iteration_rescale_factor =
static_cast<double>(reports.size()) / static_cast<double>(run_iterations);
for (const auto& Stat : *reports[0].statistics) {
// Get the data from the accumulator to BenchmarkReporter::Run's.
Run data;
data.run_name = reports[0].run_name;
data.family_index = reports[0].family_index;
data.per_family_instance_index = reports[0].per_family_instance_index;
data.run_type = BenchmarkReporter::Run::RT_Aggregate;
data.threads = reports[0].threads;
data.repetitions = reports[0].repetitions;
data.repetition_index = Run::no_repetition_index;
data.aggregate_name = Stat.name_;
data.aggregate_unit = Stat.unit_;
data.report_label = report_label;
// It is incorrect to say that an aggregate is computed over
// run's iterations, because those iterations already got averaged.
// Similarly, if there are N repetitions with 1 iterations each,
// an aggregate will be computed over N measurements, not 1.
// Thus it is best to simply use the count of separate reports.
data.iterations = static_cast<IterationCount>(reports.size());
data.real_accumulated_time = Stat.compute_(real_accumulated_time_stat);
data.cpu_accumulated_time = Stat.compute_(cpu_accumulated_time_stat);
if (data.aggregate_unit == StatisticUnit::kTime) {
// We will divide these times by data.iterations when reporting, but the
// data.iterations is not necessarily the scale of these measurements,
// because in each repetition, these timers are sum over all the iters.
// And if we want to say that the stats are over N repetitions and not
// M iterations, we need to multiply these by (N/M).
data.real_accumulated_time *= iteration_rescale_factor;
data.cpu_accumulated_time *= iteration_rescale_factor;
}
data.time_unit = reports[0].time_unit;
// user counters
for (auto const& kv : counter_stats) {
// Do *NOT* rescale the custom counters. They are already properly scaled.
const auto uc_stat = Stat.compute_(kv.second.s);
auto c = Counter(uc_stat, counter_stats[kv.first].c.flags,
counter_stats[kv.first].c.oneK);
data.counters[kv.first] = c;
}
results.push_back(data);
}
return results;
}
} // end namespace benchmark

View File

@@ -0,0 +1,44 @@
// Copyright 2016 Ismael Jimenez Martinez. All rights reserved.
// Copyright 2017 Roman Lebedev. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef STATISTICS_H_
#define STATISTICS_H_
#include <vector>
#include "benchmark/benchmark.h"
namespace benchmark {
// Return a vector containing the mean, median and standard deviation
// information (and any user-specified info) for the specified list of reports.
// If 'reports' contains less than two non-errored runs an empty vector is
// returned
BENCHMARK_EXPORT
std::vector<BenchmarkReporter::Run> ComputeStats(
const std::vector<BenchmarkReporter::Run>& reports);
BENCHMARK_EXPORT
double StatisticsMean(const std::vector<double>& v);
BENCHMARK_EXPORT
double StatisticsMedian(const std::vector<double>& v);
BENCHMARK_EXPORT
double StatisticsStdDev(const std::vector<double>& v);
BENCHMARK_EXPORT
double StatisticsCV(const std::vector<double>& v);
} // end namespace benchmark
#endif // STATISTICS_H_

View File

@@ -0,0 +1,277 @@
#include "string_util.h"
#include <array>
#ifdef BENCHMARK_STL_ANDROID_GNUSTL
#include <cerrno>
#endif
#include <cmath>
#include <cstdarg>
#include <cstdio>
#include <memory>
#include <sstream>
#include "arraysize.h"
#include "benchmark/benchmark.h"
namespace benchmark {
namespace {
// kilo, Mega, Giga, Tera, Peta, Exa, Zetta, Yotta.
const char* const kBigSIUnits[] = {"k", "M", "G", "T", "P", "E", "Z", "Y"};
// Kibi, Mebi, Gibi, Tebi, Pebi, Exbi, Zebi, Yobi.
const char* const kBigIECUnits[] = {"Ki", "Mi", "Gi", "Ti",
"Pi", "Ei", "Zi", "Yi"};
// milli, micro, nano, pico, femto, atto, zepto, yocto.
const char* const kSmallSIUnits[] = {"m", "u", "n", "p", "f", "a", "z", "y"};
// We require that all three arrays have the same size.
static_assert(arraysize(kBigSIUnits) == arraysize(kBigIECUnits),
"SI and IEC unit arrays must be the same size");
static_assert(arraysize(kSmallSIUnits) == arraysize(kBigSIUnits),
"Small SI and Big SI unit arrays must be the same size");
const int64_t kUnitsSize = arraysize(kBigSIUnits);
void ToExponentAndMantissa(double val, int precision, double one_k,
std::string* mantissa, int64_t* exponent) {
std::stringstream mantissa_stream;
if (val < 0) {
mantissa_stream << "-";
val = -val;
}
// Adjust threshold so that it never excludes things which can't be rendered
// in 'precision' digits.
const double adjusted_threshold =
std::max(1.0, 1.0 / std::pow(10.0, precision));
const double big_threshold = (adjusted_threshold * one_k) - 1;
const double small_threshold = adjusted_threshold;
// Values in ]simple_threshold,small_threshold[ will be printed as-is
const double simple_threshold = 0.01;
if (val > big_threshold) {
// Positive powers
double scaled = val;
for (size_t i = 0; i < arraysize(kBigSIUnits); ++i) {
scaled /= one_k;
if (scaled <= big_threshold) {
mantissa_stream << scaled;
*exponent = static_cast<int64_t>(i + 1);
*mantissa = mantissa_stream.str();
return;
}
}
mantissa_stream << val;
*exponent = 0;
} else if (val < small_threshold) {
// Negative powers
if (val < simple_threshold) {
double scaled = val;
for (size_t i = 0; i < arraysize(kSmallSIUnits); ++i) {
scaled *= one_k;
if (scaled >= small_threshold) {
mantissa_stream << scaled;
*exponent = -static_cast<int64_t>(i + 1);
*mantissa = mantissa_stream.str();
return;
}
}
}
mantissa_stream << val;
*exponent = 0;
} else {
mantissa_stream << val;
*exponent = 0;
}
*mantissa = mantissa_stream.str();
}
std::string ExponentToPrefix(int64_t exponent, bool iec) {
if (exponent == 0) {
return {};
}
const int64_t index = (exponent > 0 ? exponent - 1 : -exponent - 1);
if (index >= kUnitsSize) {
return {};
}
const char* const* array =
(exponent > 0 ? (iec ? kBigIECUnits : kBigSIUnits) : kSmallSIUnits);
return std::string(array[index]);
}
std::string ToBinaryStringFullySpecified(double value, int precision,
Counter::OneK one_k) {
std::string mantissa;
int64_t exponent = 0;
ToExponentAndMantissa(value, precision,
one_k == Counter::kIs1024 ? 1024.0 : 1000.0, &mantissa,
&exponent);
return mantissa + ExponentToPrefix(exponent, one_k == Counter::kIs1024);
}
std::string StrFormatImp(const char* msg, va_list args) {
// we might need a second shot at this, so pre-emptivly make a copy
va_list args_cp;
va_copy(args_cp, args);
// TODO(ericwf): use std::array for first attempt to avoid one memory
// allocation guess what the size might be
std::array<char, 256> local_buff = {};
// 2015-10-08: vsnprintf is used instead of snd::vsnprintf due to a limitation
// in the android-ndk
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wformat-nonliteral"
#endif // __GNUC__
auto ret = vsnprintf(local_buff.data(), local_buff.size(), msg, args_cp);
#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif // __GNUC__
va_end(args_cp);
// handle empty expansion
if (ret == 0) {
return {};
}
if (static_cast<std::size_t>(ret) < local_buff.size()) {
return std::string(local_buff.data());
}
// we did not provide a long enough buffer on our first attempt.
// add 1 to size to account for null-byte in size cast to prevent overflow
std::size_t size = static_cast<std::size_t>(ret) + 1;
auto buff_ptr = std::unique_ptr<char[]>(new char[size]);
// 2015-10-08: vsnprintf is used instead of snd::vsnprintf due to a limitation
// in the android-ndk
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wformat-nonliteral"
#endif // __GNUC__
vsnprintf(buff_ptr.get(), size, msg, args);
#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif // __GNUC__
return std::string(buff_ptr.get());
}
} // end namespace
std::string HumanReadableNumber(double n, Counter::OneK one_k) {
return ToBinaryStringFullySpecified(n, 1, one_k);
}
std::string StrFormat(const char* format, ...) {
va_list args;
va_start(args, format);
std::string tmp = StrFormatImp(format, args);
va_end(args);
return tmp;
}
std::vector<std::string> StrSplit(const std::string& str, char delim) {
if (str.empty()) {
return {};
}
std::vector<std::string> ret;
size_t first = 0;
size_t next = str.find(delim);
for (; next != std::string::npos;
first = next + 1, next = str.find(delim, first)) {
ret.push_back(str.substr(first, next - first));
}
ret.push_back(str.substr(first));
return ret;
}
#ifdef BENCHMARK_STL_ANDROID_GNUSTL
/*
* GNU STL in Android NDK lacks support for some C++11 functions, including
* stoul, stoi, stod. We reimplement them here using C functions strtoul,
* strtol, strtod. Note that reimplemented functions are in benchmark::
* namespace, not std:: namespace.
*/
unsigned long stoul(const std::string& str, size_t* pos, int base) {
/* Record previous errno */
const int oldErrno = errno;
errno = 0;
const char* strStart = str.c_str();
char* strEnd = const_cast<char*>(strStart);
const unsigned long result = strtoul(strStart, &strEnd, base);
const int strtoulErrno = errno;
/* Restore previous errno */
errno = oldErrno;
/* Check for errors and return */
if (strtoulErrno == ERANGE) {
throw std::out_of_range("stoul failed: " + str +
" is outside of range of unsigned long");
} else if (strEnd == strStart || strtoulErrno != 0) {
throw std::invalid_argument("stoul failed: " + str + " is not an integer");
}
if (pos != nullptr) {
*pos = static_cast<size_t>(strEnd - strStart);
}
return result;
}
int stoi(const std::string& str, size_t* pos, int base) {
/* Record previous errno */
const int oldErrno = errno;
errno = 0;
const char* strStart = str.c_str();
char* strEnd = const_cast<char*>(strStart);
const long result = strtol(strStart, &strEnd, base);
const int strtolErrno = errno;
/* Restore previous errno */
errno = oldErrno;
/* Check for errors and return */
if (strtolErrno == ERANGE || long(int(result)) != result) {
throw std::out_of_range("stoul failed: " + str +
" is outside of range of int");
} else if (strEnd == strStart || strtolErrno != 0) {
throw std::invalid_argument("stoul failed: " + str + " is not an integer");
}
if (pos != nullptr) {
*pos = static_cast<size_t>(strEnd - strStart);
}
return int(result);
}
double stod(const std::string& str, size_t* pos) {
/* Record previous errno */
const int oldErrno = errno;
errno = 0;
const char* strStart = str.c_str();
char* strEnd = const_cast<char*>(strStart);
const double result = strtod(strStart, &strEnd);
/* Restore previous errno */
const int strtodErrno = errno;
errno = oldErrno;
/* Check for errors and return */
if (strtodErrno == ERANGE) {
throw std::out_of_range("stoul failed: " + str +
" is outside of range of int");
} else if (strEnd == strStart || strtodErrno != 0) {
throw std::invalid_argument("stoul failed: " + str + " is not an integer");
}
if (pos != nullptr) {
*pos = static_cast<size_t>(strEnd - strStart);
}
return result;
}
#endif
} // end namespace benchmark

View File

@@ -0,0 +1,69 @@
#ifndef BENCHMARK_STRING_UTIL_H_
#define BENCHMARK_STRING_UTIL_H_
#include <sstream>
#include <string>
#include <utility>
#include <vector>
#include "benchmark/benchmark.h"
#include "benchmark/export.h"
#include "check.h"
namespace benchmark {
BENCHMARK_EXPORT
std::string HumanReadableNumber(double n, Counter::OneK one_k);
BENCHMARK_EXPORT
#if defined(__MINGW32__)
__attribute__((format(__MINGW_PRINTF_FORMAT, 1, 2)))
#elif defined(__GNUC__)
__attribute__((format(printf, 1, 2)))
#endif
std::string
StrFormat(const char* format, ...);
inline std::ostream& StrCatImp(std::ostream& out) BENCHMARK_NOEXCEPT {
return out;
}
template <class First, class... Rest>
inline std::ostream& StrCatImp(std::ostream& out, First&& f, Rest&&... rest) {
out << std::forward<First>(f);
return StrCatImp(out, std::forward<Rest>(rest)...);
}
template <class... Args>
inline std::string StrCat(Args&&... args) {
std::ostringstream ss;
StrCatImp(ss, std::forward<Args>(args)...);
return ss.str();
}
BENCHMARK_EXPORT
std::vector<std::string> StrSplit(const std::string& str, char delim);
// Disable lint checking for this block since it re-implements C functions.
// NOLINTBEGIN
#ifdef BENCHMARK_STL_ANDROID_GNUSTL
/*
* GNU STL in Android NDK lacks support for some C++11 functions, including
* stoul, stoi, stod. We reimplement them here using C functions strtoul,
* strtol, strtod. Note that reimplemented functions are in benchmark::
* namespace, not std:: namespace.
*/
unsigned long stoul(const std::string& str, size_t* pos = nullptr,
int base = 10);
int stoi(const std::string& str, size_t* pos = nullptr, int base = 10);
double stod(const std::string& str, size_t* pos = nullptr);
#else
using std::stod; // NOLINT(misc-unused-using-decls)
using std::stoi; // NOLINT(misc-unused-using-decls)
using std::stoul; // NOLINT(misc-unused-using-decls)
#endif
// NOLINTEND
} // end namespace benchmark
#endif // BENCHMARK_STRING_UTIL_H_

View File

@@ -0,0 +1,902 @@
// Copyright 2015 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "internal_macros.h"
#ifdef BENCHMARK_OS_WINDOWS
#if !defined(WINVER) || WINVER < 0x0600
#undef WINVER
#define WINVER 0x0600
#endif // WINVER handling
#include <shlwapi.h>
#undef StrCat // Don't let StrCat in string_util.h be renamed to lstrcatA
#include <versionhelpers.h>
#include <windows.h>
#include <codecvt>
#else
#include <fcntl.h>
#if !defined(BENCHMARK_OS_FUCHSIA) && !defined(BENCHMARK_OS_QURT)
#include <sys/resource.h>
#endif
#include <sys/time.h>
#include <sys/types.h> // this header must be included before 'sys/sysctl.h' to avoid compilation error on FreeBSD
#include <unistd.h>
#if defined BENCHMARK_OS_FREEBSD || defined BENCHMARK_OS_MACOSX || \
defined BENCHMARK_OS_NETBSD || defined BENCHMARK_OS_OPENBSD || \
defined BENCHMARK_OS_DRAGONFLY
#define BENCHMARK_HAS_SYSCTL
#include <sys/sysctl.h>
#endif
#endif
#if defined(BENCHMARK_OS_SOLARIS)
#include <kstat.h>
#include <netdb.h>
#endif
#if defined(BENCHMARK_OS_QNX)
#include <sys/syspage.h>
#endif
#if defined(BENCHMARK_OS_QURT)
#include <qurt.h>
#endif
#if defined(BENCHMARK_HAS_PTHREAD_AFFINITY)
#include <pthread.h>
#endif
#if defined(BENCHMARK_OS_LINUX)
#include <sys/personality.h>
#endif
#include <algorithm>
#include <array>
#include <bitset>
#include <cerrno>
#include <climits>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <iostream>
#include <iterator>
#include <limits>
#include <locale>
#include <memory>
#include <random>
#include <sstream>
#include <utility>
#include "benchmark/benchmark.h"
#include "check.h"
#include "cycleclock.h"
#include "log.h"
#include "string_util.h"
#include "timers.h"
namespace benchmark {
namespace {
void PrintImp(std::ostream& out) { out << '\n'; }
template <class First, class... Rest>
void PrintImp(std::ostream& out, First&& f, Rest&&... rest) {
out << std::forward<First>(f);
PrintImp(out, std::forward<Rest>(rest)...);
}
template <class... Args>
BENCHMARK_NORETURN void PrintErrorAndDie(Args&&... args) {
PrintImp(std::cerr, std::forward<Args>(args)...);
std::cerr << std::flush;
std::exit(EXIT_FAILURE);
}
#ifdef BENCHMARK_HAS_SYSCTL
/// ValueUnion - A type used to correctly alias the byte-for-byte output of
/// `sysctl` with the result type it's to be interpreted as.
struct ValueUnion {
union DataT {
int32_t int32_value;
int64_t int64_value;
// For correct aliasing of union members from bytes.
char bytes[8];
};
using DataPtr = std::unique_ptr<DataT, decltype(&std::free)>;
// The size of the data union member + its trailing array size.
std::size_t size;
DataPtr buff;
public:
ValueUnion() : size(0), buff(nullptr, &std::free) {}
explicit ValueUnion(std::size_t buff_size)
: size(sizeof(DataT) + buff_size),
buff(::new(std::malloc(size)) DataT(), &std::free) {}
ValueUnion(ValueUnion&& other) = default;
explicit operator bool() const { return bool(buff); }
char* data() const { return buff->bytes; }
std::string GetAsString() const { return std::string(data()); }
int64_t GetAsInteger() const {
if (size == sizeof(buff->int32_value))
return buff->int32_value;
else if (size == sizeof(buff->int64_value))
return buff->int64_value;
BENCHMARK_UNREACHABLE();
}
template <class T, int N>
std::array<T, N> GetAsArray() {
const int arr_size = sizeof(T) * N;
BM_CHECK_LE(arr_size, size);
std::array<T, N> arr;
std::memcpy(arr.data(), data(), arr_size);
return arr;
}
};
ValueUnion GetSysctlImp(std::string const& name) {
#if defined BENCHMARK_OS_OPENBSD
int mib[2];
mib[0] = CTL_HW;
if ((name == "hw.ncpuonline") || (name == "hw.cpuspeed")) {
ValueUnion buff(sizeof(int));
if (name == "hw.ncpuonline") {
mib[1] = HW_NCPUONLINE;
} else {
mib[1] = HW_CPUSPEED;
}
if (sysctl(mib, 2, buff.data(), &buff.size, nullptr, 0) == -1) {
return ValueUnion();
}
return buff;
}
return ValueUnion();
#else
std::size_t cur_buff_size = 0;
if (sysctlbyname(name.c_str(), nullptr, &cur_buff_size, nullptr, 0) == -1)
return ValueUnion();
ValueUnion buff(cur_buff_size);
if (sysctlbyname(name.c_str(), buff.data(), &buff.size, nullptr, 0) == 0)
return buff;
return ValueUnion();
#endif
}
BENCHMARK_MAYBE_UNUSED
bool GetSysctl(std::string const& name, std::string* out) {
out->clear();
auto buff = GetSysctlImp(name);
if (!buff) return false;
out->assign(buff.data());
return true;
}
template <class Tp,
class = typename std::enable_if<std::is_integral<Tp>::value>::type>
bool GetSysctl(std::string const& name, Tp* out) {
*out = 0;
auto buff = GetSysctlImp(name);
if (!buff) return false;
*out = static_cast<Tp>(buff.GetAsInteger());
return true;
}
template <class Tp, size_t N>
bool GetSysctl(std::string const& name, std::array<Tp, N>* out) {
auto buff = GetSysctlImp(name);
if (!buff) return false;
*out = buff.GetAsArray<Tp, N>();
return true;
}
#endif
template <class ArgT>
bool ReadFromFile(std::string const& fname, ArgT* arg) {
*arg = ArgT();
std::ifstream f(fname.c_str());
if (!f.is_open()) {
return false;
}
f >> *arg;
return f.good();
}
CPUInfo::Scaling CpuScaling(int num_cpus) {
// We don't have a valid CPU count, so don't even bother.
if (num_cpus <= 0) {
return CPUInfo::Scaling::UNKNOWN;
}
#if defined(BENCHMARK_OS_QNX)
return CPUInfo::Scaling::UNKNOWN;
#elif !defined(BENCHMARK_OS_WINDOWS)
// On Linux, the CPUfreq subsystem exposes CPU information as files on the
// local file system. If reading the exported files fails, then we may not be
// running on Linux, so we silently ignore all the read errors.
std::string res;
for (int cpu = 0; cpu < num_cpus; ++cpu) {
std::string governor_file =
StrCat("/sys/devices/system/cpu/cpu", cpu, "/cpufreq/scaling_governor");
if (ReadFromFile(governor_file, &res) && res != "performance") {
return CPUInfo::Scaling::ENABLED;
}
}
return CPUInfo::Scaling::DISABLED;
#else
return CPUInfo::Scaling::UNKNOWN;
#endif
}
int CountSetBitsInCPUMap(std::string val) {
auto CountBits = [](std::string part) {
using CPUMask = std::bitset<sizeof(std::uintptr_t) * CHAR_BIT>;
part = "0x" + part;
CPUMask mask(benchmark::stoul(part, nullptr, 16));
return static_cast<int>(mask.count());
};
std::size_t pos = 0;
int total = 0;
while ((pos = val.find(',')) != std::string::npos) {
total += CountBits(val.substr(0, pos));
val = val.substr(pos + 1);
}
if (!val.empty()) {
total += CountBits(val);
}
return total;
}
BENCHMARK_MAYBE_UNUSED
std::vector<CPUInfo::CacheInfo> GetCacheSizesFromKVFS() {
std::vector<CPUInfo::CacheInfo> res;
std::string dir = "/sys/devices/system/cpu/cpu0/cache/";
int idx = 0;
while (true) {
CPUInfo::CacheInfo info;
std::string fpath = StrCat(dir, "index", idx++, "/");
std::ifstream f(StrCat(fpath, "size").c_str());
if (!f.is_open()) {
break;
}
std::string suffix;
f >> info.size;
if (f.fail()) {
PrintErrorAndDie("Failed while reading file '", fpath, "size'");
}
if (f.good()) {
f >> suffix;
if (f.bad()) {
PrintErrorAndDie(
"Invalid cache size format: failed to read size suffix");
} else if (f && suffix != "K") {
PrintErrorAndDie("Invalid cache size format: Expected bytes ", suffix);
} else if (suffix == "K") {
info.size *= 1024;
}
}
if (!ReadFromFile(StrCat(fpath, "type"), &info.type)) {
PrintErrorAndDie("Failed to read from file ", fpath, "type");
}
if (!ReadFromFile(StrCat(fpath, "level"), &info.level)) {
PrintErrorAndDie("Failed to read from file ", fpath, "level");
}
std::string map_str;
if (!ReadFromFile(StrCat(fpath, "shared_cpu_map"), &map_str)) {
PrintErrorAndDie("Failed to read from file ", fpath, "shared_cpu_map");
}
info.num_sharing = CountSetBitsInCPUMap(map_str);
res.push_back(info);
}
return res;
}
#ifdef BENCHMARK_OS_MACOSX
std::vector<CPUInfo::CacheInfo> GetCacheSizesMacOSX() {
std::vector<CPUInfo::CacheInfo> res;
std::array<int, 4> cache_counts{{0, 0, 0, 0}};
GetSysctl("hw.cacheconfig", &cache_counts);
struct {
std::string name;
std::string type;
int level;
int num_sharing;
} cases[] = {{"hw.l1dcachesize", "Data", 1, cache_counts[1]},
{"hw.l1icachesize", "Instruction", 1, cache_counts[1]},
{"hw.l2cachesize", "Unified", 2, cache_counts[2]},
{"hw.l3cachesize", "Unified", 3, cache_counts[3]}};
for (auto& c : cases) {
int val;
if (!GetSysctl(c.name, &val)) continue;
CPUInfo::CacheInfo info;
info.type = c.type;
info.level = c.level;
info.size = val;
info.num_sharing = c.num_sharing;
res.push_back(std::move(info));
}
return res;
}
#elif defined(BENCHMARK_OS_WINDOWS)
std::vector<CPUInfo::CacheInfo> GetCacheSizesWindows() {
std::vector<CPUInfo::CacheInfo> res;
DWORD buffer_size = 0;
using PInfo = SYSTEM_LOGICAL_PROCESSOR_INFORMATION;
using CInfo = CACHE_DESCRIPTOR;
using UPtr = std::unique_ptr<PInfo, decltype(&std::free)>;
GetLogicalProcessorInformation(nullptr, &buffer_size);
UPtr buff(static_cast<PInfo*>(std::malloc(buffer_size)), &std::free);
if (!GetLogicalProcessorInformation(buff.get(), &buffer_size)) {
PrintErrorAndDie("Failed during call to GetLogicalProcessorInformation: ",
GetLastError());
}
PInfo* it = buff.get();
PInfo* end = buff.get() + (buffer_size / sizeof(PInfo));
for (; it != end; ++it) {
if (it->Relationship != RelationCache) {
continue;
}
using BitSet = std::bitset<sizeof(ULONG_PTR) * CHAR_BIT>;
BitSet b(it->ProcessorMask);
// To prevent duplicates, only consider caches where CPU 0 is specified
if (!b.test(0)) continue;
const CInfo& cache = it->Cache;
CPUInfo::CacheInfo C;
C.num_sharing = static_cast<int>(b.count());
C.level = cache.Level;
C.size = static_cast<int>(cache.Size);
C.type = "Unknown";
switch (cache.Type) {
// Windows SDK version >= 10.0.26100.0
#ifdef NTDDI_WIN11_GE
case CacheUnknown:
break;
#endif
case CacheUnified:
C.type = "Unified";
break;
case CacheInstruction:
C.type = "Instruction";
break;
case CacheData:
C.type = "Data";
break;
case CacheTrace:
C.type = "Trace";
break;
}
res.push_back(C);
}
return res;
}
#elif BENCHMARK_OS_QNX
std::vector<CPUInfo::CacheInfo> GetCacheSizesQNX() {
std::vector<CPUInfo::CacheInfo> res;
struct cacheattr_entry* cache = SYSPAGE_ENTRY(cacheattr);
uint32_t const elsize = SYSPAGE_ELEMENT_SIZE(cacheattr);
int num = SYSPAGE_ENTRY_SIZE(cacheattr) / elsize;
for (int i = 0; i < num; ++i) {
CPUInfo::CacheInfo info;
switch (cache->flags) {
case CACHE_FLAG_INSTR:
info.type = "Instruction";
info.level = 1;
break;
case CACHE_FLAG_DATA:
info.type = "Data";
info.level = 1;
break;
case CACHE_FLAG_UNIFIED:
info.type = "Unified";
info.level = 2;
break;
case CACHE_FLAG_SHARED:
info.type = "Shared";
info.level = 3;
break;
default:
continue;
break;
}
info.size = cache->line_size * cache->num_lines;
info.num_sharing = 0;
res.push_back(std::move(info));
cache = SYSPAGE_ARRAY_ADJ_OFFSET(cacheattr, cache, elsize);
}
return res;
}
#endif
std::vector<CPUInfo::CacheInfo> GetCacheSizes() {
#ifdef BENCHMARK_OS_MACOSX
return GetCacheSizesMacOSX();
#elif defined(BENCHMARK_OS_WINDOWS)
return GetCacheSizesWindows();
#elif defined(BENCHMARK_OS_QNX)
return GetCacheSizesQNX();
#elif defined(BENCHMARK_OS_QURT) || defined(__EMSCRIPTEN__)
return std::vector<CPUInfo::CacheInfo>();
#elif defined(__FRC_ROBORIO__)
return std::vector<CPUInfo::CacheInfo>();
#else
return GetCacheSizesFromKVFS();
#endif
}
std::string GetSystemName() {
#if defined(BENCHMARK_OS_WINDOWS)
std::string str;
static constexpr int COUNT = MAX_COMPUTERNAME_LENGTH + 1;
TCHAR hostname[COUNT] = {'\0'};
DWORD DWCOUNT = COUNT;
if (!GetComputerName(hostname, &DWCOUNT)) return std::string("");
#ifndef UNICODE
str = std::string(hostname, DWCOUNT);
#else
// `WideCharToMultiByte` returns `0` when conversion fails.
int len = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, hostname,
DWCOUNT, NULL, 0, NULL, NULL);
str.resize(len);
WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, hostname, DWCOUNT, &str[0],
str.size(), NULL, NULL);
#endif
return str;
#elif defined(BENCHMARK_OS_QURT)
std::string str = "Hexagon DSP";
qurt_arch_version_t arch_version_struct;
if (qurt_sysenv_get_arch_version(&arch_version_struct) == QURT_EOK) {
str += " v";
str += std::to_string(arch_version_struct.arch_version);
}
return str;
#else
#ifndef HOST_NAME_MAX
#ifdef BENCHMARK_HAS_SYSCTL // BSD/Mac doesn't have HOST_NAME_MAX defined
#define HOST_NAME_MAX 64
#elif defined(BENCHMARK_OS_NACL)
#define HOST_NAME_MAX 64
#elif defined(BENCHMARK_OS_QNX)
#define HOST_NAME_MAX 154
#elif defined(BENCHMARK_OS_RTEMS)
#define HOST_NAME_MAX 256
#elif defined(BENCHMARK_OS_SOLARIS)
#define HOST_NAME_MAX MAXHOSTNAMELEN
#elif defined(BENCHMARK_OS_ZOS)
#define HOST_NAME_MAX _POSIX_HOST_NAME_MAX
#else
#pragma message("HOST_NAME_MAX not defined. using 64")
#define HOST_NAME_MAX 64
#endif
#endif // def HOST_NAME_MAX
char hostname[HOST_NAME_MAX];
int retVal = gethostname(hostname, HOST_NAME_MAX);
return retVal != 0 ? std::string() : std::string(hostname);
#endif // Catch-all POSIX block.
}
SystemInfo::ASLR GetASLR() {
#ifdef BENCHMARK_OS_LINUX
const auto curr_personality = personality(0xffffffff);
return (curr_personality & ADDR_NO_RANDOMIZE) ? SystemInfo::ASLR::DISABLED
: SystemInfo::ASLR::ENABLED;
#else
// FIXME: support detecting ASLR on other OS.
return SystemInfo::ASLR::UNKNOWN;
#endif
}
int GetNumCPUsImpl() {
#ifdef BENCHMARK_OS_WINDOWS
SYSTEM_INFO sysinfo;
// Use memset as opposed to = {} to avoid GCC missing initializer false
// positives.
std::memset(&sysinfo, 0, sizeof(SYSTEM_INFO));
GetSystemInfo(&sysinfo);
// number of logical processors in the current group
return static_cast<int>(sysinfo.dwNumberOfProcessors);
#elif defined(BENCHMARK_OS_QNX)
return static_cast<int>(_syspage_ptr->num_cpu);
#elif defined(BENCHMARK_OS_QURT)
qurt_sysenv_max_hthreads_t hardware_threads;
if (qurt_sysenv_get_max_hw_threads(&hardware_threads) != QURT_EOK) {
hardware_threads.max_hthreads = 1;
}
return static_cast<int>(hardware_threads.max_hthreads);
#elif defined(BENCHMARK_HAS_SYSCTL)
// *BSD, macOS
int num_cpu = -1;
constexpr auto* hwncpu =
#if defined BENCHMARK_OS_MACOSX
"hw.logicalcpu";
#elif defined(HW_NCPUONLINE)
"hw.ncpuonline";
#else
"hw.ncpu";
#endif
if (GetSysctl(hwncpu, &num_cpu)) return num_cpu;
PrintErrorAndDie("Err: ", strerror(errno));
#elif defined(_SC_NPROCESSORS_ONLN)
// Linux, Solaris, AIX, Haiku, WASM, etc.
// Returns -1 in case of a failure.
int num_cpu = static_cast<int>(sysconf(_SC_NPROCESSORS_ONLN));
if (num_cpu < 0) {
PrintErrorAndDie("sysconf(_SC_NPROCESSORS_ONLN) failed with error: ",
strerror(errno));
}
return num_cpu;
#else
// Fallback, no other API exists.
return -1;
#endif
BENCHMARK_UNREACHABLE();
}
int GetNumCPUs() {
int num_cpus = GetNumCPUsImpl();
if (num_cpus < 1) {
std::cerr << "Unable to extract number of CPUs.\n";
// There must be at least one CPU on which we're running.
num_cpus = 1;
}
return num_cpus;
}
class ThreadAffinityGuard final {
public:
ThreadAffinityGuard() : reset_affinity(SetAffinity()) {
if (!reset_affinity) {
std::cerr << "***WARNING*** Failed to set thread affinity. Estimated CPU "
"frequency may be incorrect.\n";
}
}
~ThreadAffinityGuard() {
if (!reset_affinity) {
return;
}
#if defined(BENCHMARK_HAS_PTHREAD_AFFINITY)
int ret = pthread_setaffinity_np(self, sizeof(previous_affinity),
&previous_affinity);
if (ret == 0) {
return;
}
#elif defined(BENCHMARK_OS_WINDOWS_WIN32)
DWORD_PTR ret = SetThreadAffinityMask(self, previous_affinity);
if (ret != 0) {
return;
}
#endif // def BENCHMARK_HAS_PTHREAD_AFFINITY
PrintErrorAndDie("Failed to reset thread affinity");
}
ThreadAffinityGuard(ThreadAffinityGuard&&) = delete;
ThreadAffinityGuard(const ThreadAffinityGuard&) = delete;
ThreadAffinityGuard& operator=(ThreadAffinityGuard&&) = delete;
ThreadAffinityGuard& operator=(const ThreadAffinityGuard&) = delete;
private:
bool SetAffinity() {
#if defined(BENCHMARK_HAS_PTHREAD_AFFINITY)
int ret = 0;
self = pthread_self();
ret = pthread_getaffinity_np(self, sizeof(previous_affinity),
&previous_affinity);
if (ret != 0) {
return false;
}
cpu_set_t affinity;
memcpy(&affinity, &previous_affinity, sizeof(affinity));
bool is_first_cpu = true;
for (int i = 0; i < CPU_SETSIZE; ++i) {
if (CPU_ISSET(i, &affinity)) {
if (is_first_cpu) {
is_first_cpu = false;
} else {
CPU_CLR(i, &affinity);
}
}
}
if (is_first_cpu) {
return false;
}
ret = pthread_setaffinity_np(self, sizeof(affinity), &affinity);
return ret == 0;
#elif defined(BENCHMARK_OS_WINDOWS_WIN32)
self = GetCurrentThread();
DWORD_PTR mask = static_cast<DWORD_PTR>(1) << GetCurrentProcessorNumber();
previous_affinity = SetThreadAffinityMask(self, mask);
return previous_affinity != 0;
#else
return false;
#endif // def BENCHMARK_HAS_PTHREAD_AFFINITY
}
#if defined(BENCHMARK_HAS_PTHREAD_AFFINITY)
pthread_t self{};
cpu_set_t previous_affinity{};
#elif defined(BENCHMARK_OS_WINDOWS_WIN32)
HANDLE self;
DWORD_PTR previous_affinity;
#endif // def BENCHMARK_HAS_PTHREAD_AFFINITY
bool reset_affinity;
};
double GetCPUCyclesPerSecond(CPUInfo::Scaling scaling) {
// Currently, scaling is only used on linux path here,
// suppress diagnostics about it being unused on other paths.
(void)scaling;
#if defined BENCHMARK_OS_LINUX || defined BENCHMARK_OS_CYGWIN
long freq = 0;
// If the kernel is exporting the tsc frequency use that. There are issues
// where cpuinfo_max_freq cannot be relied on because the BIOS may be
// exporintg an invalid p-state (on x86) or p-states may be used to put the
// processor in a new mode (turbo mode). Essentially, those frequencies
// cannot always be relied upon. The same reasons apply to /proc/cpuinfo as
// well.
if (ReadFromFile("/sys/devices/system/cpu/cpu0/tsc_freq_khz", &freq)
// If CPU scaling is disabled, use the *current* frequency.
// Note that we specifically don't want to read cpuinfo_cur_freq,
// because it is only readable by root.
|| (scaling == CPUInfo::Scaling::DISABLED &&
ReadFromFile("/sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq",
&freq))
// Otherwise, if CPU scaling may be in effect, we want to use
// the *maximum* frequency, not whatever CPU speed some random processor
// happens to be using now.
|| ReadFromFile("/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq",
&freq)) {
// The value is in kHz (as the file name suggests). For example, on a
// 2GHz warpstation, the file contains the value "2000000".
return static_cast<double>(freq) * 1000.0;
}
const double error_value = -1;
double bogo_clock = error_value;
std::ifstream f("/proc/cpuinfo");
if (!f.is_open()) {
std::cerr << "failed to open /proc/cpuinfo\n";
return error_value;
}
auto StartsWithKey = [](std::string const& Value, std::string const& Key) {
if (Key.size() > Value.size()) {
return false;
}
auto Cmp = [&](char X, char Y) {
return std::tolower(X) == std::tolower(Y);
};
return std::equal(Key.begin(), Key.end(), Value.begin(), Cmp);
};
std::string ln;
while (std::getline(f, ln)) {
if (ln.empty()) {
continue;
}
std::size_t split_idx = ln.find(':');
std::string value;
if (split_idx != std::string::npos) {
value = ln.substr(split_idx + 1);
}
// When parsing the "cpu MHz" and "bogomips" (fallback) entries, we only
// accept positive values. Some environments (virtual machines) report zero,
// which would cause infinite looping in WallTime_Init.
if (StartsWithKey(ln, "cpu MHz")) {
if (!value.empty()) {
double cycles_per_second = benchmark::stod(value) * 1000000.0;
if (cycles_per_second > 0) {
return cycles_per_second;
}
}
} else if (StartsWithKey(ln, "bogomips")) {
if (!value.empty()) {
bogo_clock = benchmark::stod(value) * 1000000.0;
if (bogo_clock < 0.0) {
bogo_clock = error_value;
}
}
}
}
if (f.bad()) {
std::cerr << "Failure reading /proc/cpuinfo\n";
return error_value;
}
if (!f.eof()) {
std::cerr << "Failed to read to end of /proc/cpuinfo\n";
return error_value;
}
f.close();
// If we found the bogomips clock, but nothing better, we'll use it (but
// we're not happy about it); otherwise, fallback to the rough estimation
// below.
if (bogo_clock >= 0.0) {
return bogo_clock;
}
#elif defined BENCHMARK_HAS_SYSCTL
constexpr auto* freqStr =
#if defined(BENCHMARK_OS_FREEBSD) || defined(BENCHMARK_OS_NETBSD)
"machdep.tsc_freq";
#elif defined BENCHMARK_OS_OPENBSD
"hw.cpuspeed";
#elif defined BENCHMARK_OS_DRAGONFLY
"hw.tsc_frequency";
#else
"hw.cpufrequency";
#endif
unsigned long long hz = 0;
#if defined BENCHMARK_OS_OPENBSD
if (GetSysctl(freqStr, &hz)) {
return static_cast<double>(hz * 1000000);
}
#else
if (GetSysctl(freqStr, &hz)) {
return static_cast<double>(hz);
}
#endif
fprintf(stderr, "Unable to determine clock rate from sysctl: %s: %s\n",
freqStr, strerror(errno));
fprintf(stderr,
"This does not affect benchmark measurements, only the "
"metadata output.\n");
#elif defined BENCHMARK_OS_WINDOWS_WIN32
// In NT, read MHz from the registry. If we fail to do so or we're in win9x
// then make a crude estimate.
DWORD data, data_size = sizeof(data);
if (IsWindowsXPOrGreater() &&
SUCCEEDED(
SHGetValueA(HKEY_LOCAL_MACHINE,
"HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0",
"~MHz", nullptr, &data, &data_size))) {
return static_cast<double>(static_cast<int64_t>(data) *
static_cast<int64_t>(1000 * 1000)); // was mhz
}
#elif defined(BENCHMARK_OS_SOLARIS)
kstat_ctl_t* kc = kstat_open();
if (!kc) {
std::cerr << "failed to open /dev/kstat\n";
return -1;
}
kstat_t* ksp = kstat_lookup(kc, const_cast<char*>("cpu_info"), -1,
const_cast<char*>("cpu_info0"));
if (!ksp) {
std::cerr << "failed to lookup in /dev/kstat\n";
return -1;
}
if (kstat_read(kc, ksp, NULL) < 0) {
std::cerr << "failed to read from /dev/kstat\n";
return -1;
}
kstat_named_t* knp = (kstat_named_t*)kstat_data_lookup(
ksp, const_cast<char*>("current_clock_Hz"));
if (!knp) {
std::cerr << "failed to lookup data in /dev/kstat\n";
return -1;
}
if (knp->data_type != KSTAT_DATA_UINT64) {
std::cerr << "current_clock_Hz is of unexpected data type: "
<< knp->data_type << "\n";
return -1;
}
double clock_hz = knp->value.ui64;
kstat_close(kc);
return clock_hz;
#elif defined(BENCHMARK_OS_QNX)
return static_cast<double>(
static_cast<int64_t>(SYSPAGE_ENTRY(cpuinfo)->speed) *
static_cast<int64_t>(1000 * 1000));
#elif defined(BENCHMARK_OS_QURT)
// QuRT doesn't provide any API to query Hexagon frequency.
return 1000000000;
#endif
// If we've fallen through, attempt to roughly estimate the CPU clock rate.
// Make sure to use the same cycle counter when starting and stopping the
// cycle timer. We just pin the current thread to a cpu in the previous
// affinity set.
ThreadAffinityGuard affinity_guard;
static constexpr double estimate_time_s = 1.0;
const double start_time = ChronoClockNow();
const auto start_ticks = cycleclock::Now();
// Impose load instead of calling sleep() to make sure the cycle counter
// works.
using PRNG = std::minstd_rand;
using Result = PRNG::result_type;
PRNG rng(static_cast<Result>(start_ticks));
Result state = 0;
do {
static constexpr size_t batch_size = 10000;
rng.discard(batch_size);
state += rng();
} while (ChronoClockNow() - start_time < estimate_time_s);
DoNotOptimize(state);
const auto end_ticks = cycleclock::Now();
const double end_time = ChronoClockNow();
return static_cast<double>(end_ticks - start_ticks) / (end_time - start_time);
// Reset the affinity of current thread when the lifetime of affinity_guard
// ends.
}
std::vector<double> GetLoadAvg() {
#if (defined BENCHMARK_OS_FREEBSD || defined(BENCHMARK_OS_LINUX) || \
defined BENCHMARK_OS_MACOSX || defined BENCHMARK_OS_NETBSD || \
defined BENCHMARK_OS_OPENBSD || defined BENCHMARK_OS_DRAGONFLY) && \
!(defined(__ANDROID__) && __ANDROID_API__ < 29)
static constexpr int kMaxSamples = 3;
std::vector<double> res(kMaxSamples, 0.0);
const auto nelem = getloadavg(res.data(), kMaxSamples);
if (nelem < 1) {
res.clear();
} else {
res.resize(static_cast<size_t>(nelem));
}
return res;
#else
return {};
#endif
}
} // end namespace
const CPUInfo& CPUInfo::Get() {
static const CPUInfo* info = new CPUInfo();
return *info;
}
CPUInfo::CPUInfo()
: num_cpus(GetNumCPUs()),
scaling(CpuScaling(num_cpus)),
cycles_per_second(GetCPUCyclesPerSecond(scaling)),
caches(GetCacheSizes()),
load_avg(GetLoadAvg()) {}
const SystemInfo& SystemInfo::Get() {
static const SystemInfo* info = new SystemInfo();
return *info;
}
SystemInfo::SystemInfo() : name(GetSystemName()), ASLRStatus(GetASLR()) {}
} // end namespace benchmark

View File

@@ -0,0 +1,45 @@
#ifndef BENCHMARK_THREAD_MANAGER_H
#define BENCHMARK_THREAD_MANAGER_H
#include <atomic>
#include "benchmark/benchmark.h"
#include "mutex.h"
namespace benchmark {
namespace internal {
class ThreadManager {
public:
explicit ThreadManager(int num_threads) : start_stop_barrier_(num_threads) {}
Mutex& GetBenchmarkMutex() const RETURN_CAPABILITY(benchmark_mutex_) {
return benchmark_mutex_;
}
bool StartStopBarrier() { return start_stop_barrier_.wait(); }
void NotifyThreadComplete() { start_stop_barrier_.removeThread(); }
struct Result {
IterationCount iterations = 0;
double real_time_used = 0;
double cpu_time_used = 0;
double manual_time_used = 0;
int64_t complexity_n = 0;
std::string report_label_;
std::string skip_message_;
internal::Skipped skipped_ = internal::NotSkipped;
UserCounters counters;
};
GUARDED_BY(GetBenchmarkMutex()) Result results;
private:
mutable Mutex benchmark_mutex_;
Barrier start_stop_barrier_;
};
} // namespace internal
} // namespace benchmark
#endif // BENCHMARK_THREAD_MANAGER_H

View File

@@ -0,0 +1,86 @@
#ifndef BENCHMARK_THREAD_TIMER_H
#define BENCHMARK_THREAD_TIMER_H
#include "check.h"
#include "timers.h"
namespace benchmark {
namespace internal {
class ThreadTimer {
explicit ThreadTimer(bool measure_process_cpu_time_)
: measure_process_cpu_time(measure_process_cpu_time_) {}
public:
static ThreadTimer Create() {
return ThreadTimer(/*measure_process_cpu_time_=*/false);
}
static ThreadTimer CreateProcessCpuTime() {
return ThreadTimer(/*measure_process_cpu_time_=*/true);
}
// Called by each thread
void StartTimer() {
running_ = true;
start_real_time_ = ChronoClockNow();
start_cpu_time_ = ReadCpuTimerOfChoice();
}
// Called by each thread
void StopTimer() {
BM_CHECK(running_);
running_ = false;
real_time_used_ += ChronoClockNow() - start_real_time_;
// Floating point error can result in the subtraction producing a negative
// time. Guard against that.
cpu_time_used_ +=
std::max<double>(ReadCpuTimerOfChoice() - start_cpu_time_, 0);
}
// Called by each thread
void SetIterationTime(double seconds) { manual_time_used_ += seconds; }
bool running() const { return running_; }
// REQUIRES: timer is not running
double real_time_used() const {
BM_CHECK(!running_);
return real_time_used_;
}
// REQUIRES: timer is not running
double cpu_time_used() const {
BM_CHECK(!running_);
return cpu_time_used_;
}
// REQUIRES: timer is not running
double manual_time_used() const {
BM_CHECK(!running_);
return manual_time_used_;
}
private:
double ReadCpuTimerOfChoice() const {
if (measure_process_cpu_time) return ProcessCPUUsage();
return ThreadCPUUsage();
}
// should the thread, or the process, time be measured?
const bool measure_process_cpu_time;
bool running_ = false; // Is the timer running
double start_real_time_ = 0; // If running_
double start_cpu_time_ = 0; // If running_
// Accumulated time so far (does not contain current slice if running_)
double real_time_used_ = 0;
double cpu_time_used_ = 0;
// Manually set iteration time. User sets this with SetIterationTime(seconds).
double manual_time_used_ = 0;
};
} // namespace internal
} // namespace benchmark
#endif // BENCHMARK_THREAD_TIMER_H

View File

@@ -0,0 +1,288 @@
// Copyright 2015 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "timers.h"
#include "internal_macros.h"
#ifdef BENCHMARK_OS_WINDOWS
#include <shlwapi.h>
#undef StrCat // Don't let StrCat in string_util.h be renamed to lstrcatA
#include <versionhelpers.h>
#include <windows.h>
#else
#include <fcntl.h>
#if !defined(BENCHMARK_OS_FUCHSIA) && !defined(BENCHMARK_OS_QURT)
#include <sys/resource.h>
#endif
#include <sys/time.h>
#include <sys/types.h> // this header must be included before 'sys/sysctl.h' to avoid compilation error on FreeBSD
#include <unistd.h>
#if defined BENCHMARK_OS_FREEBSD || defined BENCHMARK_OS_DRAGONFLY || \
defined BENCHMARK_OS_MACOSX
#include <sys/sysctl.h>
#endif
#if defined(BENCHMARK_OS_MACOSX)
#include <mach/mach_init.h>
#include <mach/mach_port.h>
#include <mach/thread_act.h>
#endif
#if defined(BENCHMARK_OS_QURT)
#include <qurt.h>
#endif
#endif
#ifdef BENCHMARK_OS_EMSCRIPTEN
#include <emscripten.h>
#endif
#include <cerrno>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <iostream>
#include <limits>
#include <mutex>
#include "check.h"
#include "log.h"
#include "string_util.h"
namespace benchmark {
// Suppress unused warnings on helper functions.
#if defined(__GNUC__)
#pragma GCC diagnostic ignored "-Wunused-function"
#endif
#if defined(__NVCOMPILER)
#pragma diag_suppress declared_but_not_referenced
#endif
namespace {
#if defined(BENCHMARK_OS_WINDOWS)
double MakeTime(FILETIME const& kernel_time, FILETIME const& user_time) {
ULARGE_INTEGER kernel;
ULARGE_INTEGER user;
kernel.HighPart = kernel_time.dwHighDateTime;
kernel.LowPart = kernel_time.dwLowDateTime;
user.HighPart = user_time.dwHighDateTime;
user.LowPart = user_time.dwLowDateTime;
return (static_cast<double>(kernel.QuadPart) +
static_cast<double>(user.QuadPart)) *
1e-7;
}
#elif !defined(BENCHMARK_OS_FUCHSIA) && !defined(BENCHMARK_OS_QURT)
double MakeTime(struct rusage const& ru) {
return (static_cast<double>(ru.ru_utime.tv_sec) +
static_cast<double>(ru.ru_utime.tv_usec) * 1e-6 +
static_cast<double>(ru.ru_stime.tv_sec) +
static_cast<double>(ru.ru_stime.tv_usec) * 1e-6);
}
#endif
#if defined(BENCHMARK_OS_MACOSX)
double MakeTime(thread_basic_info_data_t const& info) {
return (static_cast<double>(info.user_time.seconds) +
static_cast<double>(info.user_time.microseconds) * 1e-6 +
static_cast<double>(info.system_time.seconds) +
static_cast<double>(info.system_time.microseconds) * 1e-6);
}
#endif
#if defined(CLOCK_PROCESS_CPUTIME_ID) || defined(CLOCK_THREAD_CPUTIME_ID)
double MakeTime(struct timespec const& ts) {
return static_cast<double>(ts.tv_sec) +
(static_cast<double>(ts.tv_nsec) * 1e-9);
}
#endif
BENCHMARK_NORETURN void DiagnoseAndExit(const char* msg) {
std::cerr << "ERROR: " << msg << '\n';
std::flush(std::cerr);
std::exit(EXIT_FAILURE);
}
} // end namespace
double ProcessCPUUsage() {
#if defined(BENCHMARK_OS_WINDOWS)
HANDLE proc = GetCurrentProcess();
FILETIME creation_time;
FILETIME exit_time;
FILETIME kernel_time;
FILETIME user_time;
if (GetProcessTimes(proc, &creation_time, &exit_time, &kernel_time,
&user_time))
return MakeTime(kernel_time, user_time);
DiagnoseAndExit("GetProccessTimes() failed");
#elif defined(BENCHMARK_OS_QURT)
// Note that qurt_timer_get_ticks() is no longer documented as of SDK 5.3.0,
// and doesn't appear to work on at least some devices (eg Samsung S22),
// so let's use the actually-documented and apparently-equivalent
// qurt_sysclock_get_hw_ticks() call instead.
return static_cast<double>(
qurt_timer_timetick_to_us(qurt_sysclock_get_hw_ticks())) *
1.0e-6;
#elif defined(BENCHMARK_OS_EMSCRIPTEN)
// clock_gettime(CLOCK_PROCESS_CPUTIME_ID, ...) returns 0 on Emscripten.
// Use Emscripten-specific API. Reported CPU time would be exactly the
// same as total time, but this is ok because there aren't long-latency
// synchronous system calls in Emscripten.
return emscripten_get_now() * 1e-3;
#elif defined(CLOCK_PROCESS_CPUTIME_ID) && !defined(BENCHMARK_OS_MACOSX)
// FIXME We want to use clock_gettime, but its not available in MacOS 10.11.
// See https://github.com/google/benchmark/pull/292
struct timespec spec {};
if (clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &spec) == 0) {
return MakeTime(spec);
}
DiagnoseAndExit("clock_gettime(CLOCK_PROCESS_CPUTIME_ID, ...) failed");
#else
struct rusage ru;
if (getrusage(RUSAGE_SELF, &ru) == 0) return MakeTime(ru);
DiagnoseAndExit("getrusage(RUSAGE_SELF, ...) failed");
#endif
}
double ThreadCPUUsage() {
#if defined(BENCHMARK_OS_WINDOWS)
HANDLE this_thread = GetCurrentThread();
FILETIME creation_time;
FILETIME exit_time;
FILETIME kernel_time;
FILETIME user_time;
GetThreadTimes(this_thread, &creation_time, &exit_time, &kernel_time,
&user_time);
return MakeTime(kernel_time, user_time);
#elif defined(BENCHMARK_OS_QURT)
// Note that qurt_timer_get_ticks() is no longer documented as of SDK 5.3.0,
// and doesn't appear to work on at least some devices (eg Samsung S22),
// so let's use the actually-documented and apparently-equivalent
// qurt_sysclock_get_hw_ticks() call instead.
return static_cast<double>(
qurt_timer_timetick_to_us(qurt_sysclock_get_hw_ticks())) *
1.0e-6;
#elif defined(BENCHMARK_OS_MACOSX)
// FIXME We want to use clock_gettime, but its not available in MacOS 10.11.
// See https://github.com/google/benchmark/pull/292
mach_msg_type_number_t count = THREAD_BASIC_INFO_COUNT;
thread_basic_info_data_t info;
mach_port_t thread = pthread_mach_thread_np(pthread_self());
if (thread_info(thread, THREAD_BASIC_INFO,
reinterpret_cast<thread_info_t>(&info),
&count) == KERN_SUCCESS) {
return MakeTime(info);
}
DiagnoseAndExit("ThreadCPUUsage() failed when evaluating thread_info");
#elif defined(BENCHMARK_OS_EMSCRIPTEN)
// Emscripten doesn't support traditional threads
return ProcessCPUUsage();
#elif defined(BENCHMARK_OS_RTEMS)
// RTEMS doesn't support CLOCK_THREAD_CPUTIME_ID. See
// https://github.com/RTEMS/rtems/blob/master/cpukit/posix/src/clockgettime.c
return ProcessCPUUsage();
#elif defined(BENCHMARK_OS_ZOS)
// z/OS doesn't support CLOCK_THREAD_CPUTIME_ID.
return ProcessCPUUsage();
#elif defined(BENCHMARK_OS_SOLARIS)
struct rusage ru;
if (getrusage(RUSAGE_LWP, &ru) == 0) return MakeTime(ru);
DiagnoseAndExit("getrusage(RUSAGE_LWP, ...) failed");
#elif defined(CLOCK_THREAD_CPUTIME_ID)
struct timespec ts {};
if (clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ts) == 0) {
return MakeTime(ts);
}
DiagnoseAndExit("clock_gettime(CLOCK_THREAD_CPUTIME_ID, ...) failed");
#else
#error Per-thread timing is not available on your system.
#endif
}
std::string LocalDateTimeString() {
// Write the local time in RFC3339 format yyyy-mm-ddTHH:MM:SS+/-HH:MM.
typedef std::chrono::system_clock Clock;
std::time_t now = Clock::to_time_t(Clock::now());
const std::size_t kTzOffsetLen = 6;
const std::size_t kTimestampLen = 19;
std::size_t tz_len = 0;
std::size_t timestamp_len = 0;
long int offset_minutes = 0;
char tz_offset_sign = '+';
// tz_offset is set in one of three ways:
// * strftime with %z - This either returns empty or the ISO 8601 time. The
// maximum length an
// ISO 8601 string can be is 7 (e.g. -03:30, plus trailing zero).
// * snprintf with %c%02li:%02li - The maximum length is 41 (one for %c, up to
// 19 for %02li,
// one for :, up to 19 %02li, plus trailing zero).
// * A fixed string of "-00:00". The maximum length is 7 (-00:00, plus
// trailing zero).
//
// Thus, the maximum size this needs to be is 41.
char tz_offset[41];
// Long enough buffer to avoid format-overflow warnings
char storage[128];
#if defined(BENCHMARK_OS_WINDOWS)
std::tm* timeinfo_p = ::localtime(&now);
#else
std::tm timeinfo{};
std::tm* timeinfo_p = &timeinfo;
::localtime_r(&now, &timeinfo);
#endif
tz_len = std::strftime(tz_offset, sizeof(tz_offset), "%z", timeinfo_p);
if (tz_len < kTzOffsetLen && tz_len > 1) {
// Timezone offset was written. strftime writes offset as +HHMM or -HHMM,
// RFC3339 specifies an offset as +HH:MM or -HH:MM. To convert, we parse
// the offset as an integer, then reprint it to a string.
offset_minutes = ::strtol(tz_offset, NULL, 10);
if (offset_minutes < 0) {
offset_minutes *= -1;
tz_offset_sign = '-';
}
tz_len = static_cast<size_t>(
::snprintf(tz_offset, sizeof(tz_offset), "%c%02li:%02li",
tz_offset_sign, offset_minutes / 100, offset_minutes % 100));
BM_CHECK(tz_len == kTzOffsetLen);
((void)tz_len); // Prevent unused variable warning in optimized build.
} else {
// Unknown offset. RFC3339 specifies that unknown local offsets should be
// written as UTC time with -00:00 timezone.
#if defined(BENCHMARK_OS_WINDOWS)
// Potential race condition if another thread calls localtime or gmtime.
timeinfo_p = ::gmtime(&now);
#else
::gmtime_r(&now, &timeinfo);
#endif
strncpy(tz_offset, "-00:00", kTzOffsetLen + 1);
}
timestamp_len =
std::strftime(storage, sizeof(storage), "%Y-%m-%dT%H:%M:%S", timeinfo_p);
BM_CHECK(timestamp_len == kTimestampLen);
// Prevent unused variable warning in optimized build.
((void)kTimestampLen);
std::strncat(storage, tz_offset, sizeof(storage) - timestamp_len - 1);
return std::string(storage);
}
} // end namespace benchmark

View File

@@ -0,0 +1,75 @@
#ifndef BENCHMARK_TIMERS_H
#define BENCHMARK_TIMERS_H
#include <chrono>
#include <string>
namespace benchmark {
// Return the CPU usage of the current process
double ProcessCPUUsage();
// Return the CPU usage of the children of the current process
double ChildrenCPUUsage();
// Return the CPU usage of the current thread
double ThreadCPUUsage();
#if defined(BENCHMARK_OS_QURT)
// std::chrono::now() can return 0 on some Hexagon devices;
// this reads the value of a 56-bit, 19.2MHz hardware counter
// and converts it to seconds. Unlike std::chrono, this doesn't
// return an absolute time, but since ChronoClockNow() is only used
// to compute elapsed time, this shouldn't matter.
struct QuRTClock {
typedef uint64_t rep;
typedef std::ratio<1, 19200000> period;
typedef std::chrono::duration<rep, period> duration;
typedef std::chrono::time_point<QuRTClock> time_point;
static const bool is_steady = false;
static time_point now() {
unsigned long long count;
asm volatile(" %0 = c31:30 " : "=r"(count));
return time_point(static_cast<duration>(count));
}
};
#else
#if defined(HAVE_STEADY_CLOCK)
template <bool HighResIsSteady = std::chrono::high_resolution_clock::is_steady>
struct ChooseSteadyClock {
typedef std::chrono::high_resolution_clock type;
};
template <>
struct ChooseSteadyClock<false> {
typedef std::chrono::steady_clock type;
};
#endif // HAVE_STEADY_CLOCK
#endif
struct ChooseClockType {
#if defined(BENCHMARK_OS_QURT)
typedef QuRTClock type;
#elif defined(HAVE_STEADY_CLOCK)
typedef ChooseSteadyClock<>::type type;
#else
typedef std::chrono::high_resolution_clock type;
#endif
};
inline double ChronoClockNow() {
typedef ChooseClockType::type ClockType;
using FpSeconds = std::chrono::duration<double, std::chrono::seconds::period>;
return FpSeconds(ClockType::now().time_since_epoch()).count();
}
std::string LocalDateTimeString();
} // end namespace benchmark
#endif // BENCHMARK_TIMERS_H

View File

@@ -13,14 +13,14 @@ plugins {
id 'edu.wpi.first.wpilib.versioning.WPILibVersioningPlugin' version '2023.0.1'
id 'edu.wpi.first.wpilib.repositories.WPILibRepositoriesPlugin' version '2020.2'
id 'edu.wpi.first.NativeUtils' apply false
id 'edu.wpi.first.GradleJni' version '1.1.0'
id 'edu.wpi.first.GradleJni' version '1.2.0'
id 'edu.wpi.first.GradleVsCode'
id 'idea'
id 'visual-studio'
id 'net.ltgt.errorprone' version '3.1.0' apply false
id 'com.gradleup.shadow' version '8.3.4' apply false
id 'com.diffplug.spotless' version '6.20.0' apply false
id 'com.github.spotbugs' version '6.0.2' apply false
id 'net.ltgt.errorprone' version '4.3.0' apply false
id 'com.gradleup.shadow' version '9.1.0' apply false
id 'com.diffplug.spotless' version '8.0.0' apply false
id 'com.github.spotbugs' version '6.4.2' apply false
}
wpilibVersioning.buildServerMode = project.hasProperty('buildServer')
@@ -39,11 +39,11 @@ allprojects {
}
}
buildScan {
termsOfServiceUrl = 'https://gradle.com/terms-of-service'
termsOfServiceAgree = 'yes'
publishAlways()
develocity {
buildScan {
termsOfUseUrl = "https://gradle.com/help/legal-terms-of-use"
termsOfUseAgree = "yes"
}
}
import com.github.spotbugs.snom.Effort
@@ -81,7 +81,7 @@ task libraryBuild() {}
build.dependsOn outputVersions
task copyAllOutputs(type: Copy) {
destinationDir outputsFolder
destinationDir = outputsFolder
}
build.dependsOn copyAllOutputs
@@ -170,5 +170,5 @@ ext.getCurrentArch = {
}
wrapper {
gradleVersion = '8.11'
gradleVersion = '8.14.3'
}

View File

@@ -5,9 +5,9 @@ repositories {
url = 'https://frcmaven.wpi.edu/artifactory/ex-gradle'
}
mavenCentral()
url "https://plugins.gradle.org/m2/"
url = "https://plugins.gradle.org/m2/"
}
}
dependencies {
implementation "edu.wpi.first:native-utils:2025.9.0"
implementation "edu.wpi.first:native-utils:2026.0.0"
}

View File

@@ -28,7 +28,7 @@ public class WPIJREArtifact extends MavenArtifact {
private boolean checkJreVersion = true;
private final String artifactLocation = "edu.wpi.first.jdk:roborio-2024:17.0.9u7-1"
private final String artifactLocation = "edu.wpi.first.jdk:roborio-2024:17.0.9u7-3"
@Inject
public WPIJREArtifact(String name, RemoteTarget target) {

View File

@@ -31,7 +31,7 @@ repositories {
}
dependencies {
implementation 'com.google.code.gson:gson:2.10.1'
implementation 'com.google.code.gson:gson:2.13.1'
implementation project(':wpiutil')
implementation project(':wpinet')

View File

@@ -46,7 +46,7 @@ public final class CameraServer {
private static final String kPublishName = "/CameraPublisher";
private static final class PropertyPublisher implements AutoCloseable {
@SuppressWarnings({"PMD.MissingBreakInSwitch", "PMD.ImplicitSwitchFallThrough", "fallthrough"})
@SuppressWarnings("fallthrough")
PropertyPublisher(NetworkTable table, VideoEvent event) {
String name;
String infoName;
@@ -66,7 +66,7 @@ public final class CameraServer {
break;
case kEnum:
m_choicesTopic = table.getStringArrayTopic(infoName + "/choices");
// fall through
// fallthrough
case kInteger:
m_integerValueEntry = table.getIntegerTopic(name).getEntry(0);
m_minPublisher = table.getIntegerTopic(infoName + "/min").publish();

View File

@@ -13,6 +13,7 @@ import org.opencv.core.Mat;
* @see VisionRunner
* @see VisionThread
*/
@FunctionalInterface
public interface VisionPipeline {
/**
* Processes the image input and sets the result objects. Implementations should make these

View File

@@ -46,8 +46,15 @@ macro(wpilib_target_warnings target)
# Suppress warning "enumeration types with a fixed underlying type are a
# Clang extension"
if(APPLE)
target_compile_options(${target} PRIVATE $<$<COMPILE_LANGUAGE:C>:-Wno-fixed-enum-extension>)
if(CMAKE_CXX_COMPILER_ID MATCHES "Clang" AND NOT EMSCRIPTEN)
if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 20.0)
target_compile_options(
${target}
PRIVATE $<$<COMPILE_LANGUAGE:C>:-Wno-fixed-enum-extension>
)
else()
target_compile_options(${target} PRIVATE $<$<COMPILE_LANGUAGE:C>:-Wno-c23-extensions>)
endif()
endif()
# Compress debug info with GCC

View File

@@ -38,8 +38,10 @@ includeOtherLibs {
^fmt/
^gtest/
^opencv2/
^imgui
^support/
^tcpsockets/
^wpi/
^wpigui
^wpinet/
}

View File

@@ -40,6 +40,14 @@ objc_library(
"src/main/native/include",
"src/main/native/objcpp",
],
sdk_frameworks = [
"CoreFoundation",
"AVFoundation",
"Foundation",
"CoreMedia",
"CoreVideo",
"IOKit",
],
tags = ["manual"],
deps = [
"//wpinet:wpinet.static",

View File

@@ -21,7 +21,7 @@ if(APPLE)
cscore
PROPERTIES
LINK_FLAGS
"-framework CoreFoundation -framework AVFoundation -framework Foundation -framework CoreMedia -framework CoreVideo"
"-framework CoreFoundation -framework AVFoundation -framework Foundation -framework CoreMedia -framework CoreVideo -framework IOKit"
)
elseif(MSVC)
target_sources(cscore PRIVATE ${cscore_windows_src})

View File

@@ -60,13 +60,8 @@ model {
if (!it.buildable || !(it instanceof NativeBinarySpec)) {
return
}
if (it.component.name == "${nativeName}JNI") {
lib project: ':wpinet', library: 'wpinet', linkage: 'static'
lib project: ':wpiutil', library: 'wpiutil', linkage: 'static'
} else {
lib project: ':wpinet', library: 'wpinet', linkage: 'shared'
lib project: ':wpiutil', library: 'wpiutil', linkage: 'shared'
}
lib project: ':wpinet', library: 'wpinet', linkage: 'shared'
lib project: ':wpiutil', library: 'wpiutil', linkage: 'shared'
}
}
}

View File

@@ -5,7 +5,6 @@
#include <opencv2/core/core.hpp>
#include <wpi/print.h>
#include "cscore.h"
#include "cscore_cv.h"
int main() {

Some files were not shown because too many files have changed in this diff Show More