VMConfigSystemViewController.m 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. //
  2. // Copyright © 2019 osy. All rights reserved.
  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. #import <mach/mach.h>
  17. #import <mach/mach_host.h>
  18. #import <sys/sysctl.h>
  19. #import "UIViewController+Extensions.h"
  20. #import "VMConfigSystemViewController.h"
  21. #import "UTMQemuConfiguration+Constants.h"
  22. #import "UTMQemuConfiguration+System.h"
  23. #import "UTMJailbreak.h"
  24. #import "UTMLogging.h"
  25. #import "VMConfigPickerView.h"
  26. #import "VMConfigTextField.h"
  27. #import "VMConfigTogglePickerCell.h"
  28. const NSUInteger kMBinBytes = 1024 * 1024;
  29. const NSUInteger kMinCodeGenBufferSizeMB = 1;
  30. const NSUInteger kMaxCodeGenBufferSizeMB = 2048;
  31. const NSUInteger kBaseUsageBytes = 128 * kMBinBytes;
  32. const float kMemoryAlertThreshold = 0.5;
  33. const float kMemoryWarningThreshold = 0.8;
  34. @interface VMConfigSystemViewController ()
  35. @end
  36. @implementation VMConfigSystemViewController {
  37. NSUInteger _totalRam;
  38. NSUInteger _estimatedRam;
  39. NSUInteger _cpuCores;
  40. }
  41. - (void)viewDidLoad {
  42. [super viewDidLoad];
  43. [self updateHostInfo];
  44. [self updateEstimatedRam];
  45. }
  46. - (void)viewWillAppear:(BOOL)animated {
  47. [super viewWillAppear:animated];
  48. if (self.configuration.systemJitCacheSize.integerValue == 0) {
  49. self.jitCacheSizeField.text = @"";
  50. }
  51. }
  52. #pragma mark - Properties
  53. @synthesize totalRam = _totalRam;
  54. @synthesize estimatedRam = _estimatedRam;
  55. #pragma mark - Picker delegate
  56. - (void)pickerCell:(VMConfigTogglePickerCell *)cell showPicker:(BOOL)visible animated:(BOOL)animated {
  57. if (visible && cell.picker == self.targetPicker) {
  58. NSUInteger index = [[UTMQemuConfiguration supportedTargetsForArchitecture:self.configuration.systemArchitecture] indexOfObject:cell.detailTextLabel.text];
  59. if (index != NSNotFound) {
  60. [cell.picker selectRow:index inComponent:0 animated:NO];
  61. }
  62. }
  63. [super pickerCell:cell showPicker:visible animated:animated];
  64. }
  65. - (NSInteger)pickerView:(nonnull UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component {
  66. NSAssert(component == 0, @"Invalid component");
  67. if (pickerView == self.targetPicker) {
  68. return [UTMQemuConfiguration supportedTargetsForArchitecture:self.configuration.systemArchitecture].count;
  69. } else {
  70. return [super pickerView:pickerView numberOfRowsInComponent:component];
  71. }
  72. }
  73. - (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component {
  74. NSAssert(component == 0, @"Invalid component");
  75. if (pickerView == self.targetPicker) {
  76. return [UTMQemuConfiguration supportedTargetsForArchitecturePretty:self.configuration.systemArchitecture][row];
  77. } else {
  78. return [super pickerView:pickerView titleForRow:row forComponent:component];
  79. }
  80. }
  81. - (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component {
  82. NSAssert(component == 0, @"Invalid component");
  83. if (pickerView == self.architecturePicker) {
  84. NSString *prev = self.configuration.systemArchitecture;
  85. [super pickerView:pickerView didSelectRow:row inComponent:component];
  86. // refresh system picker with default target
  87. if (![prev isEqualToString:self.configuration.systemArchitecture]) {
  88. NSInteger index = [UTMQemuConfiguration defaultTargetIndexForArchitecture:self.configuration.systemArchitecture];
  89. [self.targetPicker reloadAllComponents];
  90. [self.targetPicker selectRow:index inComponent:0 animated:YES];
  91. [self pickerView:self.targetPicker didSelectRow:index inComponent:0];
  92. }
  93. } else if (pickerView == self.targetPicker) {
  94. NSAssert([pickerView isKindOfClass:[VMConfigPickerView class]], @"Invalid picker");
  95. VMConfigPickerView *vmPicker = (VMConfigPickerView *)pickerView;
  96. NSString *selected = [UTMQemuConfiguration supportedTargetsForArchitecture:self.configuration.systemArchitecture][row];
  97. [self.configuration setValue:selected forKey:vmPicker.selectedOptionCell.configurationPath];
  98. vmPicker.selectedOptionCell.detailTextLabel.text = selected;
  99. } else {
  100. [super pickerView:pickerView didSelectRow:row inComponent:component];
  101. }
  102. }
  103. #pragma mark - Validate input
  104. - (void)verifyRam {
  105. if (self.estimatedRam > kMemoryWarningThreshold * self.totalRam) {
  106. [self showAlert:NSLocalizedString(@"Warning: iOS will kill apps that use more than 80% of the device's total memory.", @"VMConfigSystemViewController") actions:nil completion:nil];
  107. } else if (self.estimatedRam > kMemoryAlertThreshold * self.totalRam) {
  108. [self showAlert:NSLocalizedString(@"The total memory usage is close to your device's limit. iOS will kill the VM if it consumes too much memory.", @"VMConfigSystemViewController") actions:nil completion:nil];
  109. }
  110. }
  111. - (BOOL)memorySizeFieldValid:(UITextField *)sender {
  112. BOOL valid = NO;
  113. NSAssert(sender == self.memorySizeField, @"Invalid sender");
  114. self.memorySize = sender.text.integerValue;
  115. if (self.memorySize > 0) {
  116. valid = YES;
  117. } else {
  118. [self showAlert:NSLocalizedString(@"Invalid memory size.", @"VMConfigSystemViewController") actions:nil completion:nil];
  119. }
  120. [self updateEstimatedRam];
  121. [self verifyRam];
  122. return valid;
  123. }
  124. - (BOOL)cpuCountFieldValid:(UITextField *)sender {
  125. BOOL valid = NO;
  126. NSAssert(sender == self.cpuCountField, @"Invalid sender");
  127. self.cpuCount = sender.text.integerValue;
  128. if (self.cpuCount >= 0) {
  129. valid = YES;
  130. } else {
  131. [self showAlert:NSLocalizedString(@"Invalid core count.", @"VMConfigSystemViewController") actions:nil completion:nil];
  132. }
  133. return valid;
  134. }
  135. - (BOOL)jitCacheSizeFieldValid:(UITextField *)sender {
  136. BOOL valid = NO;
  137. NSAssert(sender == self.jitCacheSizeField, @"Invalid sender");
  138. self.jitCacheSize = sender.text.integerValue;
  139. if (self.jitCacheSize == 0) { // default value
  140. valid = YES;
  141. } else if (self.jitCacheSize < kMinCodeGenBufferSizeMB) {
  142. [self showAlert:NSLocalizedString(@"JIT cache size too small.", @"VMConfigSystemViewController") actions:nil completion:nil];
  143. } else if (self.jitCacheSize > kMaxCodeGenBufferSizeMB) {
  144. [self showAlert:NSLocalizedString(@"JIT cache size cannot be larger than 2GB.", @"VMConfigSystemViewController") actions:nil completion:nil];
  145. } else {
  146. valid = YES;
  147. }
  148. [self updateEstimatedRam];
  149. [self verifyRam];
  150. return valid;
  151. }
  152. - (IBAction)configTextFieldEditEnd:(VMConfigTextField *)sender {
  153. if (sender == self.memorySizeField) {
  154. if ([self memorySizeFieldValid:sender]) {
  155. [super configTextFieldEditEnd:sender];
  156. }
  157. } else if (sender == self.cpuCountField) {
  158. if ([self cpuCountFieldValid:sender]) {
  159. [super configTextFieldEditEnd:sender];
  160. }
  161. } else if (sender == self.jitCacheSizeField) {
  162. if ([self jitCacheSizeFieldValid:sender]) {
  163. [super configTextFieldEditEnd:sender];
  164. }
  165. } else {
  166. [super configTextFieldEditEnd:sender];
  167. }
  168. }
  169. #pragma mark - Update host information
  170. - (void)updateHostInfo {
  171. mach_port_t host_port;
  172. mach_msg_type_number_t host_size;
  173. vm_size_t pagesize;
  174. host_port = mach_host_self();
  175. host_size = sizeof(vm_statistics_data_t) / sizeof(integer_t);
  176. host_page_size(host_port, &pagesize);
  177. vm_statistics_data_t vm_stat;
  178. if (host_statistics(host_port, HOST_VM_INFO, (host_info_t)&vm_stat, &host_size) != KERN_SUCCESS) {
  179. UTMLog(@"Failed to fetch vm statistics");
  180. }
  181. /* Stats in bytes */
  182. natural_t mem_used = (vm_stat.free_count +
  183. vm_stat.active_count +
  184. vm_stat.inactive_count +
  185. vm_stat.wire_count) * (natural_t)pagesize;
  186. _totalRam = mem_used;
  187. /* Get core count */
  188. size_t len;
  189. unsigned int ncpu;
  190. len = sizeof(ncpu);
  191. sysctlbyname("hw.ncpu", &ncpu, &len, NULL, 0);
  192. _cpuCores = ncpu;
  193. // update labels
  194. self.totalRamLabel.text = [NSString stringWithFormat:@"%lu MB", _totalRam / kMBinBytes];
  195. self.cpuCoresLabel.text = [NSString stringWithFormat:@"%lu", _cpuCores];
  196. }
  197. - (void)updateEstimatedRam {
  198. NSUInteger guestRam = self.memorySize * kMBinBytes;
  199. NSUInteger jitSize = self.jitCacheSize * kMBinBytes;
  200. if (jitSize == 0) { // default size
  201. jitSize = guestRam / 4;
  202. }
  203. // we need to double observed JIT size due to iOS restrictions
  204. if (!jb_has_jit_entitlement()) {
  205. jitSize *= 2;
  206. }
  207. _estimatedRam = kBaseUsageBytes + guestRam + jitSize;
  208. self.estimatedRamLabel.text = [NSString stringWithFormat:@"%lu MB", _estimatedRam / kMBinBytes];
  209. }
  210. @end