/*
- * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+ * Copyright (c) 2015-2020 Samsung Electronics Co., Ltd. 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.
* @brief A crippled abstraction of widely praised, but often misused communication mechanism
*/
+#include <poll.h>
#include <stdexcept>
#include <unistd.h>
}
void SynchronizationPipe::post() {
- RUNNER_ASSERT_MSG(m_epClaimed == true, "Endpoint not claimed");
- auto ret = TEMP_FAILURE_RETRY(write(m_writeEp, "#", 1));
- RUNNER_ASSERT_ERRNO_MSG(ret > 0, "Write failed ret = " << ret);
+ post("#", 1);
}
void SynchronizationPipe::wait() {
+ char dummy;
+ wait(&dummy, 1);
+}
+
+void SynchronizationPipe::pollForWait() {
+ RUNNER_ASSERT_MSG(m_epClaimed == true, "Endpoint not claimed");
+
+ pollfd fds[1];
+ fds->fd = m_readEp;
+ fds->events = POLLIN;
+ auto ret = TEMP_FAILURE_RETRY(poll(fds, 1, -1));
+ RUNNER_ASSERT_ERRNO(ret > 0);
+}
+
+void SynchronizationPipe::post(const void *data, size_t size) {
+ RUNNER_ASSERT_MSG(m_epClaimed == true, "Endpoint not claimed");
+ auto ret = TEMP_FAILURE_RETRY(write(m_writeEp, data, size));
+ RUNNER_ASSERT_ERRNO_MSG(ret > 0 && size_t(ret) == size, "Write failed size = " << size << " ret = " << ret);
+}
+
+void SynchronizationPipe::wait(void *data, size_t size) {
RUNNER_ASSERT_MSG(m_epClaimed == true, "Endpoint not claimed");
- char buf;
- auto ret = TEMP_FAILURE_RETRY(read(m_readEp, &buf, 1));
- RUNNER_ASSERT_ERRNO_MSG(ret > 0, "Read failed ret = " << ret);
+ auto ret = TEMP_FAILURE_RETRY(read(m_readEp, data, size));
+ RUNNER_ASSERT_ERRNO_MSG(ret > 0 && size_t(ret) == size, "Read failed size = " << size << " ret = " << ret);
}
/*
- * Copyright (c) 2016-2019 Samsung Electronics Co., Ltd. All rights reserved
+ * Copyright (c) 2016-2020 Samsung Electronics Co., Ltd. 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.
* limitations under the License.
*/
+#include <poll.h>
#include <sys/smack.h>
#include <sys/capability.h>
#include <sys/prctl.h>
+#include <sys/eventfd.h>
+#include <cmath>
#include <thread>
#include <string>
#include <memory>
Api::cleanupApp(app.getAppId().c_str(), tmpUser.getUid(), pid);
}
}
+
+namespace {
+class Timestamp {
+ uint64_t _;
+ explicit Timestamp(uint64_t ts) : _(ts) {}
+public:
+ Timestamp operator-(const Timestamp &other) const {
+ RUNNER_ASSERT(_ > other._);
+ return Timestamp(_ - other._);
+ }
+ Timestamp operator+(const Timestamp &other) const {
+ return Timestamp(_ + other._);
+ }
+ bool operator<(const Timestamp &other) const {
+ return _ < other._;
+ }
+ Timestamp() = default;
+ static Timestamp future(uint64_t ns) {
+ timespec ts;
+ const auto res = clock_gettime(CLOCK_MONOTONIC_RAW, &ts);
+ RUNNER_ASSERT_ERRNO(!res);
+ return Timestamp(ts.tv_sec * 1000000000ULL + ts.tv_nsec + ns);
+ }
+ static Timestamp now() {
+ return future(0);
+ }
+ template <size_t nLowDigitsToSkip = 3>
+ static void report(Timestamp *ts, size_t n) {
+ std::sort(ts, ts+n);
+ uint64_t sum = 0, mn = -1, mx = 0;
+ long double qsum = 0;
+ for (size_t i = 0; i < n; i++) {
+ const auto t = ts[i]._;
+ sum += t;
+ qsum += decltype(qsum)(t) * t;
+ mn = std::min(mn, t);
+ mx = std::max(mx, t);
+ }
+ uint64_t avg = sum / n;
+ auto qstddev = qsum/n - decltype(qsum)(avg)*avg;
+ const auto out = [](const char *desc, uint64_t t) {
+ char raw[20];
+ char s[20 + 20/3 + 1];
+ size_t j = 0, i = 0;
+ do
+ raw[j++] = '0' + t % 10ULL;
+ while (t /= 10ULL);
+ for (;;) {
+ s[i++] = raw[--j];
+ if (j <= nLowDigitsToSkip)
+ break;
+ if (!(j % 3))
+ s[i++] = ' ';
+ }
+ s[i] = '\0';
+ std::cerr << desc << s;
+ };
+ out("min ", mn);
+ out(" max ", mx);
+ out(" avg ", avg);
+ out(" median ", ts[n/2]._);
+ out(" stddev ", std::floor(std::sqrt(qstddev)));
+ std::cerr << '\n';
+ }
+};
+
+template <class T, size_t N>
+constexpr size_t arraySize(T (&)[N]) { return N; }
+} // namespace
+
+RUNNER_TEST(security_manager_200_prepare_app_perf)
+{
+ constexpr int8_t nThreads = 32;
+ constexpr int8_t nConcurrentAppsSamples[] = { 0 /* 1 app w/ nThreads */, 1, 2, 4, 8, 16, 32 };
+ constexpr uint64_t minTotalBenchTime = 60 * 1000ULL*1000*1000; // 60s
+
+ TemporaryTestUser tmpUser(APP_TEST_USER, GUM_USERTYPE_NORMAL, false);
+ tmpUser.create();
+
+ struct App {
+ AppInstallHelper hlp;
+ pid_t pid;
+ };
+
+ std::vector<Timestamp> candidate, prepare, everything;
+
+ std::vector<App> apps;
+ std::vector<ScopedInstaller> appInstalls;
+
+ constexpr auto nAppsMax = nConcurrentAppsSamples[arraySize(nConcurrentAppsSamples) - 1] ?: 1;
+ apps.reserve(nAppsMax);
+ appInstalls.reserve(nAppsMax);
+
+ const auto uid = tmpUser.getUid();
+ for (int i = 0; i < nAppsMax; i++) {
+ apps.emplace_back(App{AppInstallHelper("app200_" + std::to_string(i), uid), 0});
+ auto &hlp = apps.back().hlp;
+ for (const auto &p : { EXTERNAL_STORAGE_PRIVILEGE, MEDIA_STORAGE_PRIVILEGE,
+ std::string("http://tizen.org/privilege/camera"),
+ std::string("http://tizen.org/privilege/internet") })
+ hlp.addPrivilege(p);
+ hlp.createSharedRODir();
+ appInstalls.emplace_back(ScopedInstaller(hlp));
+ }
+
+ for (const auto nConcurrentAppsDesc : nConcurrentAppsSamples) {
+ const auto nConcurrentApps = nConcurrentAppsDesc ?: 1;
+ const auto timeout = Timestamp::future(minTotalBenchTime / arraySize(nConcurrentAppsSamples));
+ do {
+ SynchronizationPipe synchPipe;
+ auto exitEvFd = eventfd(0, 0);
+ RUNNER_ASSERT(exitEvFd >= 0);
+
+ for (int i = 0; i < nConcurrentApps; i++) {
+ auto &app = apps[i];
+ const auto pid = fork();
+ RUNNER_ASSERT_ERRNO_MSG(pid >= 0, "Fork failed");
+ if (pid)
+ app.pid = pid;
+ else {
+ synchPipe.claimChildEp();
+ RUNNER_ASSERT_ERRNO_MSG(setLauncherSecurityAttributes(tmpUser) == 0, "launcher failed");
+
+ const auto appId = app.hlp.getAppId();
+
+ synchPipe.post(); // declare readiness to start measuring
+ synchPipe.pollForWait(); // wait for parent to signal all kids simultaneously
+
+ const auto candBeg = Timestamp::now();
+ Api::prepareAppCandidate();
+ const auto candEnd = Timestamp::now();
+
+ if (!nConcurrentAppsDesc) {
+ for (int i = 0; i < nThreads; i++)
+ std::thread([]{ for (;;) usleep(1000); }).detach();
+ }
+
+ const auto prepBeg = Timestamp::now();
+ Api::prepareApp(appId);
+ const auto prepEnd = Timestamp::now();
+
+ const Timestamp ts[2] = { candEnd-candBeg, prepEnd-prepBeg };
+ synchPipe.post(ts, sizeof ts); // post measurement payload
+
+ // stay idle until all kids are done to simulate idle apps and reduce benchmark noise
+ pollfd fds[1];
+ fds->fd = exitEvFd;
+ fds->events = POLLIN;
+ auto ret = TEMP_FAILURE_RETRY(poll(fds, 1, -1));
+ RUNNER_ASSERT_ERRNO(ret > 0);
+
+ exit(0);
+ }
+ }
+ synchPipe.claimParentEp();
+
+ for (int i = 0; i < nConcurrentApps; i++) // wait for all kids to be ready to start measurement
+ synchPipe.wait();
+ synchPipe.post(); // signal all kids to start measuring
+
+ for (int i = 0; i < nConcurrentApps; i++) {
+ Timestamp ts[2];
+ synchPipe.wait(ts, sizeof ts);
+ candidate.emplace_back(ts[0]);
+ prepare.emplace_back(ts[1]);
+ everything.emplace_back(ts[0] + ts[1]);
+ }
+
+ RUNNER_ASSERT(!eventfd_write(exitEvFd, 1)); // signal all kids to exit now
+
+ for (int i = 0; i < nConcurrentApps; i++) {
+ const auto &app = apps[i];
+ waitPid(app.pid);
+ Api::cleanupApp(app.hlp.getAppId(), uid, app.pid);
+ }
+ } while (Timestamp::now() < timeout);
+
+ if (!nConcurrentAppsDesc)
+ std::cerr << "additionalThreads " << int(nThreads) << ' ';
+ std::cerr << "nConcurrentApps " << int(nConcurrentApps) << " samples " << candidate.size() << '\n';
+ std::cerr << " prepareAppCandidate [us]: ";
+ Timestamp::report(candidate.data(), candidate.size());
+ std::cerr << " prepareApp [us]: ";
+ Timestamp::report(prepare.data(), prepare.size());
+ std::cerr << " prepareAppCandidate + prepareApp [us]: ";
+ Timestamp::report(everything.data(), everything.size());
+ candidate.clear();
+ prepare.clear();
+ everything.clear();
+ }
+}