1 /*
2  * Copyright 2023 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include <jni.h>
18 #include <asm/unistd.h>
19 #include <memory>
20 #include <android/log.h>
21 #include "Profiler.h"
22 #include <iostream>
23 #include <sys/syscall.h>
24 
25 #pragma clang diagnostic push
26 #pragma ide diagnostic ignored "UnusedParameter"
27 
28 const int32_t CountersLongCount = sizeof(utils::Profiler::Counters) / sizeof(uint64_t);
29 
30 static_assert(
31         CountersLongCount == 19,
32         "Expected Counters to have consistent length, "
33         "may need to update Kotlin LongArray definition"
34 );
35 
perf_event_open(perf_event_attr * hw_event,pid_t pid,int cpu,int group_fd,unsigned long flags)36 static int perf_event_open(perf_event_attr *hw_event, pid_t pid,
37                            int cpu, int group_fd, unsigned long flags) {
38     return (int) syscall(__NR_perf_event_open, hw_event, pid, cpu, group_fd, flags);
39 }
40 
41 #pragma clang diagnostic pop
42 extern "C"
43 JNIEXPORT jstring JNICALL
Java_androidx_benchmark_CpuCounterJni_checkPerfEventSupport(JNIEnv * env,jobject thiz)44 Java_androidx_benchmark_CpuCounterJni_checkPerfEventSupport(
45         JNIEnv *env,
46         jobject thiz
47 ) {
48 
49     // perf event group creation code copied from Profiler.cpp to allow us to
50     // return an error string on failure instead of killing process
51     perf_event_attr pe{};
52     pe.type = PERF_TYPE_HARDWARE;
53     pe.size = sizeof(perf_event_attr);
54     pe.config = PERF_COUNT_HW_INSTRUCTIONS;
55     pe.disabled = 1;
56     pe.exclude_kernel = 1;
57     pe.exclude_hv = 1;
58     pe.read_format = PERF_FORMAT_GROUP |
59                      PERF_FORMAT_ID |
60                      PERF_FORMAT_TOTAL_TIME_ENABLED |
61                      PERF_FORMAT_TOTAL_TIME_RUNNING;
62     int fd = perf_event_open(&pe, 0, -1, -1, 0);
63     // TODO: implement checkPerfEventSupport()
64     if (fd == -1) {
65         char output[256];
66         sprintf(&output[0], "perf_event_open failed: [%d]%s", errno, strerror(errno));
67         return (jstring) env->NewStringUTF(&output[0]);
68     } else {
69         close(fd);
70         return (jstring) nullptr;
71     }
72 }
73 
74 extern "C"
75 JNIEXPORT jlong JNICALL
Java_androidx_benchmark_CpuCounterJni_newProfiler(JNIEnv * env,jobject thiz)76 Java_androidx_benchmark_CpuCounterJni_newProfiler(
77         JNIEnv *env,
78         jobject thiz
79 ) {
80     auto *pProfiler = new utils::Profiler();
81     return (long) pProfiler;
82 }
83 
84 extern "C"
85 JNIEXPORT void JNICALL
Java_androidx_benchmark_CpuCounterJni_freeProfiler(JNIEnv * env,jobject thiz,jlong profiler_ptr)86 Java_androidx_benchmark_CpuCounterJni_freeProfiler(
87         JNIEnv *env,
88         jobject thiz,
89         jlong profiler_ptr
90 ) {
91     auto *pProfiler = (utils::Profiler *) profiler_ptr;
92     delete pProfiler;
93 }
94 extern "C"
95 JNIEXPORT jint JNICALL
Java_androidx_benchmark_CpuCounterJni_resetEvents(JNIEnv * env,jobject thiz,jlong profiler_ptr,jint event_mask)96 Java_androidx_benchmark_CpuCounterJni_resetEvents(
97         JNIEnv *env,
98         jobject thiz,
99         jlong profiler_ptr,
100         jint event_mask
101 ) {
102     auto *pProfiler = (utils::Profiler *) profiler_ptr;
103     return (jint) pProfiler->resetEvents(event_mask);
104 }
105 extern "C"
106 JNIEXPORT void JNICALL
Java_androidx_benchmark_CpuCounterJni_reset(JNIEnv * env,jobject thiz,jlong profiler_ptr)107 Java_androidx_benchmark_CpuCounterJni_reset(
108         JNIEnv *env,
109         jobject thiz,
110         jlong profiler_ptr
111 ) {
112     auto *pProfiler = (utils::Profiler *) profiler_ptr;
113     pProfiler->reset();
114 }
115 extern "C"
116 JNIEXPORT void JNICALL
Java_androidx_benchmark_CpuCounterJni_start(JNIEnv * env,jobject thiz,jlong profiler_ptr)117 Java_androidx_benchmark_CpuCounterJni_start(
118         JNIEnv *env,
119         jobject thiz,
120         jlong profiler_ptr
121 ) {
122     auto *pProfiler = (utils::Profiler *) profiler_ptr;
123     pProfiler->start();
124 }
125 extern "C"
126 JNIEXPORT void JNICALL
Java_androidx_benchmark_CpuCounterJni_stop(JNIEnv * env,jobject thiz,jlong profiler_ptr)127 Java_androidx_benchmark_CpuCounterJni_stop(
128         JNIEnv *env,
129         jobject thiz,
130         jlong profiler_ptr
131 ) {
132     auto *pProfiler = (utils::Profiler *) profiler_ptr;
133     pProfiler->stop();
134 }
135 extern "C"
136 JNIEXPORT void JNICALL
Java_androidx_benchmark_CpuCounterJni_read(JNIEnv * env,jobject thiz,jlong profiler_ptr,jlongArray out_data)137 Java_androidx_benchmark_CpuCounterJni_read(
138         JNIEnv *env,
139         jobject thiz,
140         jlong profiler_ptr,
141         jlongArray out_data
142 ) {
143     auto *pProfiler = (utils::Profiler *) profiler_ptr;
144     utils::Profiler::Counters counters = pProfiler->readCounters();
145     jsize longCount = sizeof(utils::Profiler::Counters) / sizeof(uint64_t);
146     env->SetLongArrayRegion(out_data, 0, longCount, reinterpret_cast<jlong *>(&counters));
147 }