Implement missing stuff in System.cpp
Bug: 171711491
Change-Id: Iabbc3aa497f5a64261d5854ad46df0a56487e34e
diff --git a/base/CMakeLists.txt b/base/CMakeLists.txt
index cb68c53..7e9c223 100644
--- a/base/CMakeLists.txt
+++ b/base/CMakeLists.txt
@@ -15,11 +15,13 @@
PathUtils.cpp
ring_buffer.c
SharedLibrary.cpp
+ StringFormat.cpp
Stream.cpp
StreamSerializing.cpp
SubAllocator.cpp
System.cpp
Tracing.cpp)
+
set(gfxstream-base-posix-sources
SharedMemory_posix.cpp
Thread_pthread.cpp)
@@ -27,17 +29,22 @@
SharedMemory_win32.cpp
Thread_win32.cpp
Win32UnicodeString.cpp)
-if (WIN32)
-add_library(
- gfxstream-base
- ${gfxstream-base-common-sources}
- ${gfxstream-base-windows-sources})
+if (APPLE)
+ set(gfxstream-platform-sources
+ ${gfxstream-base-posix-sources}
+ system-native-mac.mm)
+elseif (WIN32)
+ set(gfxstream-platform-sources
+ ${gfxstream-base-windows-sources})
else()
+ set(gfxstream-platform-sources
+ ${gfxstream-base-posix-sources})
+endif()
+
add_library(
gfxstream-base
${gfxstream-base-common-sources}
- ${gfxstream-base-posix-sources})
-endif()
+ ${gfxstream-platform-sources})
target_link_libraries(
gfxstream-base PRIVATE lz4)
diff --git a/base/StringFormat.cpp b/base/StringFormat.cpp
new file mode 100644
index 0000000..17f7dbe
--- /dev/null
+++ b/base/StringFormat.cpp
@@ -0,0 +1,81 @@
+// Copyright 2014 The Android Open Source Project
+//
+// This software is licensed under the terms of the GNU General Public
+// License version 2, as published by the Free Software Foundation, and
+// may be copied, distributed, and modified under those terms.
+//
+// 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 General Public License for more details.
+
+#include "base/StringFormat.h"
+
+#include <stdio.h>
+
+namespace android {
+namespace base {
+
+std::string StringFormatRaw(const char* format, ...) {
+ va_list args;
+ va_start(args, format);
+ auto result = StringFormatWithArgs(format, args);
+ va_end(args);
+ return result;
+}
+
+std::string StringFormatWithArgs(const char* format, va_list args) {
+ std::string result;
+ StringAppendFormatWithArgs(&result, format, args);
+ return result;
+}
+
+void StringAppendFormatRaw(std::string* string, const char* format, ...) {
+ va_list args;
+ va_start(args, format);
+ StringAppendFormatWithArgs(string, format, args);
+ va_end(args);
+}
+
+void StringAppendFormatWithArgs(std::string* string,
+ const char* format,
+ va_list args) {
+ size_t cur_size = string->size();
+ size_t extra = 0;
+ for (;;) {
+ va_list args2;
+ va_copy(args2, args);
+ int ret = vsnprintf(&(*string)[cur_size], extra, format, args2);
+ va_end(args2);
+
+ if (ret == 0) {
+ // Nothing to do here.
+ break;
+ }
+
+ if (ret > 0) {
+ size_t ret_sz = static_cast<size_t>(ret);
+ if (extra == 0) {
+ // First pass, resize the string and try again.
+ extra = ret_sz + 1;
+ string->resize(cur_size + extra);
+ continue;
+ }
+ if (ret_sz < extra) {
+ // Second pass or later, success!
+ string->resize(cur_size + ret_sz);
+ return;
+ }
+ }
+
+ // NOTE: The MSVCRT.DLL implementation of snprintf() is broken and
+ // will return -1 in case of truncation. This code path is taken
+ // when this happens, or when |ret_sz| is equal or larger than
+ // |extra|. Grow the buffer to allow for more room, then try again.
+ extra += (extra >> 1) + 32;
+ string->resize(cur_size + extra);
+ }
+}
+
+} // namespace base
+} // namespace android
diff --git a/base/StringFormat.h b/base/StringFormat.h
new file mode 100644
index 0000000..893f139
--- /dev/null
+++ b/base/StringFormat.h
@@ -0,0 +1,78 @@
+// Copyright 2014 The Android Open Source Project
+//
+// This software is licensed under the terms of the GNU General Public
+// License version 2, as published by the Free Software Foundation, and
+// may be copied, distributed, and modified under those terms.
+//
+// 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 General Public License for more details.
+
+#pragma once
+
+#include <string>
+#include <type_traits>
+#include <utility>
+
+#include <stdarg.h>
+
+namespace android {
+namespace base {
+
+// Create a new string instance that contains the printf-style formatted
+// output from |format| and potentially any following arguments.
+std::string StringFormatRaw(const char* format, ...);
+
+// A variant of StringFormat() which uses a va_list to list formatting
+// parameters instead.
+std::string StringFormatWithArgs(const char* format, va_list args);
+
+// Appends a formatted string at the end of an existing string.
+// |string| is the target string instance, |format| the format string,
+// followed by any formatting parameters. This is more efficient than
+// appending the result of StringFormat(format,...) to |*string| directly.
+void StringAppendFormatRaw(std::string* string, const char* format, ...);
+
+// A variant of StringAppendFormat() that takes a va_list to list
+// formatting parameters.
+void StringAppendFormatWithArgs(std::string* string,
+ const char* format,
+ va_list args);
+
+// unpackFormatArg() is a set of overloaded functions needed to unpack
+// an argument of the formatting list to a POD value which can be passed
+// into the sprintf()-like C function
+
+// Anything which can be used to construct a string goes here and unpacks into
+// a const char*
+inline const char* unpackFormatArg(const std::string& str) {
+ return str.c_str();
+}
+
+// Forward all PODs as-is
+template <class T>
+constexpr T&& unpackFormatArg(T&& t,
+ typename std::enable_if<
+ std::is_pod<typename std::decay<T>::type>::value
+ >::type* = nullptr) {
+ return std::forward<T>(t);
+}
+
+// These templated versions of StringFormat*() allow one to pass all kinds of
+// string objects into the argument list
+template <class... Args>
+std::string StringFormat(const char* format, Args&&... args) {
+ return StringFormatRaw(format, unpackFormatArg(std::forward<Args>(args))...);
+}
+
+template <class... Args>
+void StringAppendFormat(std::string* string,
+ const char* format,
+ Args&&... args) {
+ StringAppendFormatRaw(string, format,
+ unpackFormatArg(std::forward<Args>(args))...);
+}
+
+} // namespace base
+} // namespace android
diff --git a/base/StringFormat_unittest.cpp b/base/StringFormat_unittest.cpp
new file mode 100644
index 0000000..bc19704
--- /dev/null
+++ b/base/StringFormat_unittest.cpp
@@ -0,0 +1,78 @@
+// Copyright 2014 The Android Open Source Project
+//
+// This software is licensed under the terms of the GNU General Public
+// License version 2, as published by the Free Software Foundation, and
+// may be copied, distributed, and modified under those terms.
+//
+// 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 General Public License for more details.
+
+#include "android/base/StringFormat.h"
+
+#include <gtest/gtest.h>
+
+namespace android {
+namespace base {
+
+TEST(StringFormat, EmptyString) {
+ std::string s = StringFormat("");
+ EXPECT_TRUE(s.empty());
+ EXPECT_STREQ("", s.c_str());
+}
+
+TEST(StringFormat, SimpleString) {
+ std::string s = StringFormat("foobar");
+ EXPECT_STREQ("foobar", s.c_str());
+}
+
+TEST(StringFormat, SimpleDecimal) {
+ std::string s = StringFormat("Pi is about %d.%d\n", 3, 1415);
+ EXPECT_STREQ("Pi is about 3.1415\n", s.c_str());
+}
+
+TEST(StringFormat, VeryLongString) {
+ static const char kPiece[] = "A hospital bed is a parked taxi with the meter running - Groucho Marx. ";
+ const size_t kPieceLen = sizeof(kPiece) - 1U;
+ std::string s = StringFormat("%s%s%s%s%s%s%s", kPiece, kPiece, kPiece,
+ kPiece, kPiece, kPiece, kPiece);
+ EXPECT_EQ(7U * kPieceLen, s.size());
+ for (size_t n = 0; n < 7U; ++n) {
+ std::string s2 = std::string(s.c_str() + n * kPieceLen, kPieceLen);
+ EXPECT_STREQ(kPiece, s2.c_str()) << "Index #" << n;
+ }
+}
+
+TEST(StringAppendFormat, EmptyString) {
+ std::string s = "foo";
+ StringAppendFormat(&s, "");
+ EXPECT_EQ(3U, s.size());
+ EXPECT_STREQ("foo", s.c_str());
+}
+
+TEST(StringAppendFormat, SimpleString) {
+ std::string s = "foo";
+ StringAppendFormat(&s, "bar");
+ EXPECT_EQ(6U, s.size());
+ EXPECT_STREQ("foobar", s.c_str());
+}
+
+TEST(StringAppendFormat, VeryLongString) {
+ static const char kPiece[] = "A hospital bed is a parked taxi with the meter running - Groucho Marx. ";
+ const size_t kPieceLen = sizeof(kPiece) - 1U;
+ const size_t kCount = 12;
+ std::string s;
+ for (size_t n = 0; n < kCount; ++n) {
+ StringAppendFormat(&s, "%s", kPiece);
+ }
+
+ EXPECT_EQ(kCount * kPieceLen, s.size());
+ for (size_t n = 0; n < kCount; ++n) {
+ std::string s2 = std::string(s.c_str() + n * kPieceLen, kPieceLen);
+ EXPECT_STREQ(kPiece, s2.c_str()) << "Index #" << n;
+ }
+}
+
+} // namespace base
+} // namespace android
diff --git a/base/System.cpp b/base/System.cpp
index 6306778..88fd21f 100644
--- a/base/System.cpp
+++ b/base/System.cpp
@@ -1,5 +1,82 @@
+#include "base/StringFormat.h"
#include "base/System.h"
+#ifdef _WIN32
+#include <windows.h>
+#endif
+
+#ifdef _MSC_VER
+#include "msvc-posix.h"
+#include "dirent.h"
+#else
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <unistd.h>
+#endif
+
+#include <string.h>
+
+namespace {
+
+struct TickCountImpl {
+private:
+ uint64_t mStartTimeUs;
+#ifdef _WIN32
+ long long mFreqPerSec = 0; // 0 means 'high perf counter isn't available'
+#elif defined(__APPLE__)
+ clock_serv_t mClockServ;
+#endif
+
+public:
+ TickCountImpl() {
+#ifdef _WIN32
+ LARGE_INTEGER freq;
+ if (::QueryPerformanceFrequency(&freq)) {
+ mFreqPerSec = freq.QuadPart;
+ }
+#elif defined(__APPLE__)
+ host_get_clock_service(mach_host_self(), SYSTEM_CLOCK, &mClockServ);
+#endif
+ mStartTimeUs = getUs();
+ }
+
+#ifdef __APPLE__
+ ~TickCountImpl() {
+ mach_port_deallocate(mach_task_self(), mClockServ);
+ }
+#endif
+
+ uint64_t getStartTimeUs() const {
+ return mStartTimeUs;
+ }
+
+ uint64_t getUs() const {
+#ifdef _WIN32
+ if (!mFreqPerSec) {
+ return ::GetTickCount() * 1000;
+ }
+ LARGE_INTEGER now;
+ ::QueryPerformanceCounter(&now);
+ return (now.QuadPart * 1000000ull) / mFreqPerSec;
+#elif defined __linux__
+ timespec ts;
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+ return ts.tv_sec * 1000000ll + ts.tv_nsec / 1000;
+#else // APPLE
+ mach_timespec_t mts;
+ clock_get_time(mClockServ, &mts);
+ return mts.tv_sec * 1000000ll + mts.tv_nsec / 1000;
+#endif
+ }
+};
+
+// This is, maybe, the only static variable that may not be a LazyInstance:
+// it holds the actual timestamp at startup, and has to be initialized as
+// soon as possible after the application launch.
+static const TickCountImpl kTickCount;
+
+} // namespace
+
namespace android {
namespace base {
@@ -7,12 +84,162 @@
return {};
}
-void setEnvironmentVariable(const std::string& key) {
+void setEnvironmentVariable(const std::string& key, const std::string& value) {
+#ifdef _WIN32
+ std::string envStr =
+ StringFormat("%s=%s", key, value);
+ // Note: this leaks the result of release().
+ _wputenv(Win32UnicodeString(envStr).release());
+#else
+ if (value.empty()) {
+ unsetenv(key.c_str());
+ } else {
+ setenv(key.c_str(), value.c_str(), 1);
+ }
+#endif
}
bool isVerboseLogging() {
return false;
}
+void sleepMs(uint64_t n) {
+#ifdef _WIN32
+ ::Sleep(n);
+#else
+ usleep(n * 1000);
+#endif
+}
+
+void sleepUs(uint64_t n) {
+#ifdef _WIN32
+ ::Sleep(n / 1000);
+#else
+ usleep(n);
+#endif
+}
+
+uint64_t getUnixTimeUs() {
+ timeval tv;
+ gettimeofday(&tv, nullptr);
+ return tv.tv_sec * 1000000LL + tv.tv_usec;
+}
+
+uint64_t getHighResTimeUs() {
+ return kTickCount.getUs();
+}
+
+uint64_t getUptimeMs() {
+ return (kTickCount.getUs() - kTickCount.getStartTimeUs()) / 1000;
+}
+
+std::string getProgramDirectoryFromPlatform() {
+ std::string res;
+#if defined(__linux__)
+ char path[1024];
+ memset(path, 0, sizeof(path)); // happy valgrind!
+ int len = readlink("/proc/self/exe", path, sizeof(path));
+ if (len > 0 && len < (int)sizeof(path)) {
+ char* x = ::strrchr(path, '/');
+ if (x) {
+ *x = '\0';
+ res.assign(path);
+ }
+ }
+#elif defined(__APPLE__)
+ char s[PATH_MAX];
+ auto pid = getpid();
+ proc_pidpath(pid, s, sizeof(s));
+ char* x = ::strrchr(s, '/');
+ if (x) {
+ // skip all slashes - there might be more than one
+ while (x > s && x[-1] == '/') {
+ --x;
+ }
+ *x = '\0';
+ res.assign(s);
+ } else {
+ res.assign("<unknown-application-dir>");
+ }
+#elif defined(_WIN32)
+ Win32UnicodeString appDir(PATH_MAX);
+ int len = GetModuleFileNameW(0, appDir.data(), appDir.size());
+ res.assign("<unknown-application-dir>");
+ if (len > 0) {
+ if (len > (int)appDir.size()) {
+ appDir.resize(static_cast<size_t>(len));
+ GetModuleFileNameW(0, appDir.data(), appDir.size());
+ }
+ std::string dir = appDir.toString();
+ char* sep = ::strrchr(&dir[0], '\\');
+ if (sep) {
+ *sep = '\0';
+ res.assign(dir.c_str());
+ }
+ }
+#else
+#error "Unsupported platform!"
+#endif
+ return res;
+}
+std::string getProgramDirectory() {
+ static std::string progDir;
+ if (progDir.empty()) {
+ progDir.assign(getProgramDirectoryFromPlatform());
+ }
+ return progDir;
+}
+
+std::string getLauncherDirectory() {
+ return getProgramDirectory();
+}
+
+CpuTime cpuTime() {
+ CpuTime res;
+
+ res.wall_time_us = kTickCount.getUs();
+
+#ifdef __APPLE__
+ cpuUsageCurrentThread_macImpl(
+ &res.user_time_us,
+ &res.system_time_us);
+#else
+
+#ifdef __linux__
+ struct rusage usage;
+ getrusage(RUSAGE_THREAD, &usage);
+ res.user_time_us =
+ usage.ru_utime.tv_sec * 1000000ULL +
+ usage.ru_utime.tv_usec;
+ res.system_time_us =
+ usage.ru_stime.tv_sec * 1000000ULL +
+ usage.ru_stime.tv_usec;
+#else // Windows
+ FILETIME creation_time_struct;
+ FILETIME exit_time_struct;
+ FILETIME kernel_time_struct;
+ FILETIME user_time_struct;
+ GetThreadTimes(
+ GetCurrentThread(),
+ &creation_time_struct,
+ &exit_time_struct,
+ &kernel_time_struct,
+ &user_time_struct);
+ (void)creation_time_struct;
+ (void)exit_time_struct;
+ uint64_t user_time_100ns =
+ user_time_struct.dwLowDateTime |
+ ((uint64_t)user_time_struct.dwHighDateTime << 32);
+ uint64_t system_time_100ns =
+ kernel_time_struct.dwLowDateTime |
+ ((uint64_t)kernel_time_struct.dwHighDateTime << 32);
+ res.user_time_us = user_time_100ns / 10;
+ res.system_time_us = system_time_100ns / 10;
+#endif
+
+#endif
+ return res;
+}
+
} // namespace base
} // namespace android
diff --git a/base/system-native-mac.mm b/base/system-native-mac.mm
new file mode 100644
index 0000000..873b1ff
--- /dev/null
+++ b/base/system-native-mac.mm
@@ -0,0 +1,165 @@
+// Copyright (C) 2017 The Android Open Source Project
+//
+// This software is licensed under the terms of the GNU General Public
+// License version 2, as published by the Free Software Foundation, and
+// may be copied, distributed, and modified under those terms.
+//
+// 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 General Public License for more details.
+
+#include "android/base/Optional.h"
+#include "android/base/system/System.h"
+
+#include <Cocoa/Cocoa.h>
+#include <CoreFoundation/CoreFoundation.h>
+#include <IOKit/IOBSD.h>
+#include <IOKit/IOKitLib.h>
+#include <Carbon/Carbon.h>
+
+#include <IOKit/kext/KextManager.h>
+#include <IOKit/storage/IOBlockStorageDevice.h>
+
+#import <Foundation/Foundation.h>
+#import <Foundation/NSProcessInfo.h>
+
+#include <mach/mach_init.h>
+#include <mach/thread_act.h>
+#include <mach/mach_port.h>
+
+#include <stdlib.h>
+#include <sys/stat.h>
+
+namespace android {
+namespace base {
+
+// From:
+// https://stackoverflow.com/questions/49677034/thread-sleeps-too-long-on-os-x-when-in-background
+void disableAppNap_macImpl(void) {
+ if ([[NSProcessInfo processInfo]
+ respondsToSelector:@selector(beginActivityWithOptions:reason:)]) {
+ [[NSProcessInfo processInfo]
+ beginActivityWithOptions:0x00FFFFFF
+ reason:@"Not sleepy and don't want to nap"];
+ }
+}
+
+// based on https://stackoverflow.com/questions/13893134/get-current-pthread-cpu-usage-mac-os-x
+void cpuUsageCurrentThread_macImpl(
+ uint64_t* user,
+ uint64_t* sys) {
+
+ kern_return_t kr;
+ mach_msg_type_number_t count;
+ mach_port_t thread;
+ thread_basic_info_data_t info;
+
+ thread = mach_thread_self();
+
+ count = THREAD_BASIC_INFO_COUNT;
+ kr = thread_info(
+ thread,
+ THREAD_BASIC_INFO,
+ (thread_info_t)&info,
+ &count);
+
+ if (kr == KERN_SUCCESS) {
+ *user =
+ info.user_time.seconds * 1000000ULL +
+ info.user_time.microseconds;
+ *sys =
+ info.system_time.seconds * 1000000ULL +
+ info.system_time.microseconds;
+ } else {
+ fprintf(stderr, "%s: WARNING: could not get thread info for CPU usage\n", __func__);
+ }
+
+ mach_port_deallocate(mach_task_self(), thread);
+}
+
+Optional<System::DiskKind> nativeDiskKind(int st_dev) {
+ const char* devName = devname(st_dev, S_IFBLK);
+ if (!devName) {
+ return {};
+ }
+ CFMutableDictionaryRef classesToMatch =
+ IOBSDNameMatching(kIOMasterPortDefault, 0, devName);
+ if (!classesToMatch) {
+ NSLog(@"Could not find io classes of disk");
+ return {};
+ }
+
+ // get iterator of matching services
+ io_iterator_t entryIterator;
+
+ if (KERN_SUCCESS != IOServiceGetMatchingServices(kIOMasterPortDefault,
+ classesToMatch,
+ &entryIterator)) {
+ NSLog(@"Can't iterate services");
+ return {};
+ }
+
+ // iterate over all found medias
+ io_object_t serviceEntry, parentMedia;
+ while ((serviceEntry = IOIteratorNext(entryIterator)) != 0) {
+ // We assume there won't be more levels of nesting here. The limit is
+ // arbitrary and can be increased if we hit it.
+ int maxlevels = 8;
+ do {
+ kern_return_t kernResult = IORegistryEntryGetParentEntry(
+ serviceEntry, kIOServicePlane, &parentMedia);
+ IOObjectRelease(serviceEntry);
+
+ if (KERN_SUCCESS != kernResult) {
+ serviceEntry = 0;
+ NSLog(@"Error while getting parent service entry");
+ break;
+ }
+
+ serviceEntry = parentMedia;
+ if (!parentMedia) {
+ break; // finished iterator
+ }
+
+ CFTypeRef res = IORegistryEntryCreateCFProperty(
+ serviceEntry, CFSTR(kIOPropertyDeviceCharacteristicsKey),
+ kCFAllocatorDefault, 0);
+ if (res) {
+ NSString* type = [(NSDictionary*)res
+ objectForKey:(id)CFSTR(kIOPropertyMediumTypeKey)];
+ if ([@kIOPropertyMediumTypeSolidStateKey
+ isEqualToString:type]) {
+ CFRelease(res);
+ return System::DiskKind::Ssd;
+ } else if ([@kIOPropertyMediumTypeRotationalKey
+ isEqualToString:type]) {
+ CFRelease(res);
+ return System::DiskKind::Hdd;
+ }
+ CFRelease(res);
+ }
+ } while (maxlevels--);
+
+ if (serviceEntry) {
+ IOObjectRelease(serviceEntry);
+ }
+ }
+ IOObjectRelease(entryIterator);
+
+ return {};
+}
+
+// From:
+// https://stackoverflow.com/questions/6796028/start-a-gui-process-in-mac-os-x-without-dock-icon
+void hideDockIcon_macImpl(void) {
+ if (NSApp == nil) {
+ // Initialize the global variable "NSApp"
+ [NSApplication sharedApplication];
+ }
+ [NSApp setActivationPolicy:NSApplicationActivationPolicyAccessory];
+
+}
+
+} // namespace base
+} // namespace android
diff --git a/stream-servers/CMakeLists.txt b/stream-servers/CMakeLists.txt
index 394aaba..938d0f4 100644
--- a/stream-servers/CMakeLists.txt
+++ b/stream-servers/CMakeLists.txt
@@ -44,7 +44,8 @@
RendererImpl.cpp
FrameBuffer.cpp
GfxStreamAgents.cpp
- GfxStreamBackend.cpp)
+ GfxStreamBackend.cpp
+ virtio-gpu-gfxstream-renderer.cpp)
if (APPLE)
set(gfxstream_backend-platform-sources NativeSubWindow_cocoa.m)
elseif (WIN32)
@@ -90,6 +91,7 @@
PRIVATE
OSWindow
gfxstream_backend
+ gfxstream-base
gtest_main)