Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "build/build_config.h" | 5 #include "build/build_config.h" |
| 6 | 6 |
| 7 #include <fcntl.h> | 7 #include <fcntl.h> |
| 8 #include <mach/mach_vm.h> | |
| 8 #include <sys/mman.h> | 9 #include <sys/mman.h> |
| 9 | 10 |
| 10 #include "base/command_line.h" | 11 #include "base/command_line.h" |
| 11 #include "base/files/file_util.h" | 12 #include "base/files/file_util.h" |
| 12 #include "base/files/scoped_file.h" | 13 #include "base/files/scoped_file.h" |
| 13 #include "base/files/scoped_temp_dir.h" | 14 #include "base/files/scoped_temp_dir.h" |
| 14 #include "base/mac/mac_util.h" | 15 #include "base/mac/mac_util.h" |
| 16 #include "base/mac/mach_logging.h" | |
| 15 #include "base/memory/scoped_ptr.h" | 17 #include "base/memory/scoped_ptr.h" |
| 16 #include "base/memory/shared_memory.h" | 18 #include "base/memory/shared_memory.h" |
| 19 #include "base/strings/string_number_conversions.h" | |
| 20 #include "base/synchronization/spin_wait.h" | |
| 21 #include "base/time/time.h" | |
| 17 #include "ipc/attachment_broker_messages.h" | 22 #include "ipc/attachment_broker_messages.h" |
| 18 #include "ipc/attachment_broker_privileged_mac.h" | 23 #include "ipc/attachment_broker_privileged_mac.h" |
| 19 #include "ipc/attachment_broker_unprivileged_mac.h" | 24 #include "ipc/attachment_broker_unprivileged_mac.h" |
| 20 #include "ipc/ipc_listener.h" | 25 #include "ipc/ipc_listener.h" |
| 21 #include "ipc/ipc_message.h" | 26 #include "ipc/ipc_message.h" |
| 22 #include "ipc/ipc_test_base.h" | 27 #include "ipc/ipc_test_base.h" |
| 23 #include "ipc/ipc_test_messages.h" | 28 #include "ipc/ipc_test_messages.h" |
| 24 #include "ipc/test_util_mac.h" | 29 #include "ipc/test_util_mac.h" |
| 25 | 30 |
| 26 namespace { | 31 namespace { |
| 27 | 32 |
| 28 const char kDataBuffer1[] = "This is some test data to write to the file."; | 33 const char kDataBuffer1[] = "This is some test data to write to the file."; |
| 29 const char kDataBuffer2[] = "The lazy dog and a fox."; | 34 const char kDataBuffer2[] = "The lazy dog and a fox."; |
| 30 const char kDataBuffer3[] = "Two green bears but not a potato."; | 35 const char kDataBuffer3[] = "Two green bears but not a potato."; |
| 31 const char kDataBuffer4[] = "Red potato is best potato."; | 36 const char kDataBuffer4[] = "Red potato is best potato."; |
| 32 const std::string g_service_switch_name = "service_name"; | 37 const std::string g_service_switch_name = "service_name"; |
| 38 const size_t g_large_message_size = 8 * 1024 * 1024; | |
| 39 const int g_large_message_count = 1000; | |
| 40 const size_t g_medium_message_size = 512 * 1024; | |
| 41 | |
| 42 // Running the message loop is expected to increase the number of resident | |
| 43 // pages. The exact amount is non-deterministic, but for a simple test suite | |
| 44 // like this one, the increase is expected to be less than 1 MB. | |
| 45 const size_t g_expected_memory_increase = 1024 * 1024; | |
| 33 | 46 |
| 34 enum TestResult { | 47 enum TestResult { |
| 35 RESULT_UNKNOWN, | 48 RESULT_UNKNOWN, |
| 36 RESULT_SUCCESS, | 49 RESULT_SUCCESS, |
| 37 RESULT_FAILURE, | 50 RESULT_FAILURE, |
| 38 }; | 51 }; |
| 39 | 52 |
| 53 mach_vm_size_t GetResidentSize() { | |
| 54 task_basic_info_64 info; | |
| 55 mach_msg_type_number_t count = TASK_BASIC_INFO_64_COUNT; | |
| 56 kern_return_t kr = task_info(mach_task_self(), TASK_BASIC_INFO_64, | |
| 57 reinterpret_cast<task_info_t>(&info), &count); | |
| 58 MACH_CHECK(kr == KERN_SUCCESS, kr) << "Couldn't get resident size."; | |
| 59 | |
| 60 return info.resident_size; | |
| 61 } | |
| 62 | |
| 40 base::mac::ScopedMachSendRight GetMachPortFromBrokeredAttachment( | 63 base::mac::ScopedMachSendRight GetMachPortFromBrokeredAttachment( |
| 41 const scoped_refptr<IPC::BrokerableAttachment>& attachment) { | 64 const scoped_refptr<IPC::BrokerableAttachment>& attachment) { |
| 42 if (attachment->GetType() != | 65 if (attachment->GetType() != |
| 43 IPC::BrokerableAttachment::TYPE_BROKERABLE_ATTACHMENT) { | 66 IPC::BrokerableAttachment::TYPE_BROKERABLE_ATTACHMENT) { |
| 44 LOG(INFO) << "Attachment type not TYPE_BROKERABLE_ATTACHMENT."; | 67 LOG(INFO) << "Attachment type not TYPE_BROKERABLE_ATTACHMENT."; |
| 45 return base::mac::ScopedMachSendRight(MACH_PORT_NULL); | 68 return base::mac::ScopedMachSendRight(MACH_PORT_NULL); |
| 46 } | 69 } |
| 47 | 70 |
| 48 if (attachment->GetBrokerableType() != IPC::BrokerableAttachment::MACH_PORT) { | 71 if (attachment->GetBrokerableType() != IPC::BrokerableAttachment::MACH_PORT) { |
| 49 LOG(INFO) << "Brokerable type not MACH_PORT."; | 72 LOG(INFO) << "Brokerable type not MACH_PORT."; |
| (...skipping 361 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 411 } | 434 } |
| 412 | 435 |
| 413 void CheckChildResult() { | 436 void CheckChildResult() { |
| 414 ASSERT_EQ(ProxyListener::MESSAGE_RECEIVED, | 437 ASSERT_EQ(ProxyListener::MESSAGE_RECEIVED, |
| 415 get_proxy_listener()->get_reason()); | 438 get_proxy_listener()->get_reason()); |
| 416 ASSERT_EQ(get_result_listener()->get_result(), RESULT_SUCCESS); | 439 ASSERT_EQ(get_result_listener()->get_result(), RESULT_SUCCESS); |
| 417 } | 440 } |
| 418 | 441 |
| 419 void FinalCleanUp() { | 442 void FinalCleanUp() { |
| 420 // There should be no leaked names. | 443 // There should be no leaked names. |
| 444 SPIN_FOR_TIMEDELTA_OR_UNTIL_TRUE( | |
| 445 base::TimeDelta::FromSeconds(10), | |
| 446 active_names_at_start_ == IPC::GetActiveNameCount()); | |
| 421 EXPECT_EQ(active_names_at_start_, IPC::GetActiveNameCount()); | 447 EXPECT_EQ(active_names_at_start_, IPC::GetActiveNameCount()); |
| 422 | 448 |
| 423 // Close the channel so the client's OnChannelError() gets fired. | 449 // Close the channel so the client's OnChannelError() gets fired. |
| 424 channel()->Close(); | 450 channel()->Close(); |
| 425 | 451 |
| 426 EXPECT_TRUE(WaitForClientShutdown()); | 452 EXPECT_TRUE(WaitForClientShutdown()); |
| 427 DestroyChannel(); | 453 DestroyChannel(); |
| 428 broker_.reset(); | 454 broker_.reset(); |
| 429 } | 455 } |
| 430 | 456 |
| (...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 474 // Channels. | 500 // Channels. |
| 475 struct ChildProcessGlobals { | 501 struct ChildProcessGlobals { |
| 476 MockPortProvider port_provider; | 502 MockPortProvider port_provider; |
| 477 | 503 |
| 478 // The broker must be destroyed before the port_provider, so that the broker | 504 // The broker must be destroyed before the port_provider, so that the broker |
| 479 // gets a chance to unregister itself as an observer. This doesn't matter | 505 // gets a chance to unregister itself as an observer. This doesn't matter |
| 480 // outside of tests, since neither port_provider nor broker will ever be | 506 // outside of tests, since neither port_provider nor broker will ever be |
| 481 // destroyed. | 507 // destroyed. |
| 482 scoped_ptr<IPC::AttachmentBrokerPrivilegedMac> broker; | 508 scoped_ptr<IPC::AttachmentBrokerPrivilegedMac> broker; |
| 483 base::mac::ScopedMachSendRight server_task_port; | 509 base::mac::ScopedMachSendRight server_task_port; |
| 510 | |
| 511 // Total resident memory before running the message loop. | |
| 512 mach_vm_size_t initial_resident_size; | |
| 513 | |
| 514 // Whether to emit log statements while processing messages. | |
| 515 bool message_logging; | |
| 484 }; | 516 }; |
| 485 | 517 |
| 486 using OnMessageReceivedCallback = void (*)(IPC::Sender* sender, | 518 using OnMessageReceivedCallback = void (*)(IPC::Sender* sender, |
| 487 const IPC::Message& message, | 519 const IPC::Message& message, |
| 488 ChildProcessGlobals* globals); | 520 ChildProcessGlobals* globals); |
| 489 | 521 |
| 490 // Sets up the Mach communication ports with the server. Returns a set of | 522 // Sets up the Mach communication ports with the server. Returns a set of |
| 491 // globals that must live at least as long as the test. | 523 // globals that must live at least as long as the test. |
| 492 scoped_ptr<ChildProcessGlobals> CommonChildProcessSetUp() { | 524 scoped_ptr<ChildProcessGlobals> CommonChildProcessSetUp() { |
| 493 base::CommandLine cmd_line = *base::CommandLine::ForCurrentProcess(); | 525 base::CommandLine cmd_line = *base::CommandLine::ForCurrentProcess(); |
| 494 std::string service_name = | 526 std::string service_name = |
| 495 cmd_line.GetSwitchValueASCII(g_service_switch_name); | 527 cmd_line.GetSwitchValueASCII(g_service_switch_name); |
| 496 base::mac::ScopedMachSendRight server_port( | 528 base::mac::ScopedMachSendRight server_port( |
| 497 IPC::LookupServer(service_name.c_str())); | 529 IPC::LookupServer(service_name.c_str())); |
| 498 base::mac::ScopedMachReceiveRight client_port(IPC::MakeReceivingPort()); | 530 base::mac::ScopedMachReceiveRight client_port(IPC::MakeReceivingPort()); |
| 499 | 531 |
| 500 // Send the port that this process is listening on to the server. | 532 // Send the port that this process is listening on to the server. |
| 501 IPC::SendMachPort( | 533 IPC::SendMachPort( |
| 502 server_port.get(), client_port.get(), MACH_MSG_TYPE_MAKE_SEND); | 534 server_port.get(), client_port.get(), MACH_MSG_TYPE_MAKE_SEND); |
| 503 | 535 |
| 504 // Receive the task port of the server process. | 536 // Receive the task port of the server process. |
| 505 base::mac::ScopedMachSendRight server_task_port( | 537 base::mac::ScopedMachSendRight server_task_port( |
| 506 IPC::ReceiveMachPort(client_port.get())); | 538 IPC::ReceiveMachPort(client_port.get())); |
| 507 | 539 |
| 508 scoped_ptr<ChildProcessGlobals> globals(new ChildProcessGlobals); | 540 scoped_ptr<ChildProcessGlobals> globals(new ChildProcessGlobals); |
| 509 globals->broker.reset( | 541 globals->broker.reset( |
| 510 new IPC::AttachmentBrokerPrivilegedMac(&globals->port_provider)); | 542 new IPC::AttachmentBrokerPrivilegedMac(&globals->port_provider)); |
| 511 globals->port_provider.InsertEntry(getppid(), server_task_port.get()); | 543 globals->port_provider.InsertEntry(getppid(), server_task_port.get()); |
| 512 globals->server_task_port.reset(server_task_port.release()); | 544 globals->server_task_port.reset(server_task_port.release()); |
| 545 globals->message_logging = true; | |
| 513 return globals; | 546 return globals; |
| 514 } | 547 } |
| 515 | 548 |
| 516 int CommonPrivilegedProcessMain(OnMessageReceivedCallback callback, | 549 int CommonPrivilegedProcessMain(OnMessageReceivedCallback callback, |
| 517 const char* channel_name) { | 550 const char* channel_name) { |
| 518 LOG(INFO) << "Privileged process start."; | 551 LOG(INFO) << "Privileged process start."; |
| 519 scoped_ptr<ChildProcessGlobals> globals(CommonChildProcessSetUp()); | 552 scoped_ptr<ChildProcessGlobals> globals(CommonChildProcessSetUp()); |
| 520 | 553 |
| 521 mach_msg_type_number_t active_names_at_start = IPC::GetActiveNameCount(); | 554 mach_msg_type_number_t active_names_at_start = IPC::GetActiveNameCount(); |
| 522 | 555 |
| 523 base::MessageLoopForIO main_message_loop; | 556 base::MessageLoopForIO main_message_loop; |
| 524 ProxyListener listener; | 557 ProxyListener listener; |
| 525 | 558 |
| 526 scoped_ptr<IPC::Channel> channel(IPC::Channel::CreateClient( | 559 scoped_ptr<IPC::Channel> channel(IPC::Channel::CreateClient( |
| 527 IPCTestBase::GetChannelName(channel_name), &listener)); | 560 IPCTestBase::GetChannelName(channel_name), &listener)); |
| 528 globals->broker->RegisterCommunicationChannel(channel.get()); | 561 globals->broker->RegisterCommunicationChannel(channel.get()); |
| 529 CHECK(channel->Connect()); | 562 CHECK(channel->Connect()); |
| 530 | 563 |
| 564 globals->initial_resident_size = GetResidentSize(); | |
| 565 | |
| 531 while (true) { | 566 while (true) { |
| 532 LOG(INFO) << "Privileged process spinning run loop."; | 567 if (globals->message_logging) |
| 568 LOG(INFO) << "Privileged process spinning run loop."; | |
| 533 base::MessageLoop::current()->Run(); | 569 base::MessageLoop::current()->Run(); |
| 534 ProxyListener::Reason reason = listener.get_reason(); | 570 ProxyListener::Reason reason = listener.get_reason(); |
| 535 if (reason == ProxyListener::CHANNEL_ERROR) | 571 if (reason == ProxyListener::CHANNEL_ERROR) |
| 536 break; | 572 break; |
| 537 | 573 |
| 538 while (listener.has_message()) { | 574 while (listener.has_message()) { |
| 539 LOG(INFO) << "Privileged process running callback."; | 575 if (globals->message_logging) |
| 576 LOG(INFO) << "Privileged process running callback."; | |
| 540 callback(channel.get(), listener.get_first_message(), globals.get()); | 577 callback(channel.get(), listener.get_first_message(), globals.get()); |
| 541 LOG(INFO) << "Privileged process finishing callback."; | 578 if (globals->message_logging) |
| 579 LOG(INFO) << "Privileged process finishing callback."; | |
| 542 listener.pop_first_message(); | 580 listener.pop_first_message(); |
| 543 } | 581 } |
| 544 } | 582 } |
| 545 | 583 |
| 546 if (active_names_at_start != IPC::GetActiveNameCount()) { | 584 if (active_names_at_start != IPC::GetActiveNameCount()) { |
| 547 LOG(INFO) << "Memory leak!."; | 585 LOG(INFO) << "Memory leak!."; |
| 548 } | 586 } |
| 549 LOG(INFO) << "Privileged process end."; | 587 LOG(INFO) << "Privileged process end."; |
| 550 return 0; | 588 return 0; |
| 551 } | 589 } |
| (...skipping 593 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1145 globals->port_provider.InsertEntry(pid, task_port); | 1183 globals->port_provider.InsertEntry(pid, task_port); |
| 1146 } | 1184 } |
| 1147 } | 1185 } |
| 1148 | 1186 |
| 1149 MULTIPROCESS_IPC_TEST_CLIENT_MAIN(SendSharedMemoryHandleToSelfDelayedPort) { | 1187 MULTIPROCESS_IPC_TEST_CLIENT_MAIN(SendSharedMemoryHandleToSelfDelayedPort) { |
| 1150 return CommonPrivilegedProcessMain( | 1188 return CommonPrivilegedProcessMain( |
| 1151 &SendSharedMemoryHandleToSelfDelayedPortCallback, | 1189 &SendSharedMemoryHandleToSelfDelayedPortCallback, |
| 1152 "SendSharedMemoryHandleToSelfDelayedPort"); | 1190 "SendSharedMemoryHandleToSelfDelayedPort"); |
| 1153 } | 1191 } |
| 1154 | 1192 |
| 1193 // Tests the memory usage characteristics of attachment brokering a single large | |
| 1194 // message. This test has the *potential* to be flaky, since it compares | |
| 1195 // resident memory at different points in time, and that measurement is | |
| 1196 // non-deterministic. | |
| 1197 TEST_F(IPCAttachmentBrokerMacTest, MemoryUsageLargeMessage) { | |
| 1198 // Mach-based SharedMemory isn't support on OSX 10.6. | |
| 1199 if (base::mac::IsOSSnowLeopard()) | |
| 1200 return; | |
| 1201 | |
| 1202 CommonSetUp("MemoryUsageLargeMessage"); | |
| 1203 | |
| 1204 std::string test_string(g_large_message_size, 'a'); | |
| 1205 SendMessage1(test_string); | |
| 1206 base::MessageLoop::current()->Run(); | |
| 1207 CommonTearDown(); | |
| 1208 } | |
| 1209 | |
| 1210 void MemoryUsageLargeMessageCallback(IPC::Sender* sender, | |
| 1211 const IPC::Message& message, | |
| 1212 ChildProcessGlobals* globals) { | |
| 1213 EXPECT_LE(GetResidentSize(), | |
| 1214 globals->initial_resident_size + g_expected_memory_increase); | |
| 1215 | |
| 1216 base::SharedMemoryHandle shm(GetSharedMemoryHandleFromMsg1(message)); | |
| 1217 scoped_ptr<base::SharedMemory> shared_memory( | |
| 1218 MapSharedMemoryHandle(shm, false)); | |
| 1219 EXPECT_LE(GetResidentSize(), | |
| 1220 globals->initial_resident_size + g_expected_memory_increase); | |
| 1221 | |
| 1222 // Volatile prevents the compiler from optimizing out |c|. | |
| 1223 volatile char c = '\0'; | |
|
Tom Sepez
2015/11/20 17:23:03
I suspect that an aggressive optimizer may be able
erikchen
2015/11/20 19:01:20
I spent some time thinking about this, and decided
| |
| 1224 void* addr = shared_memory->memory(); | |
| 1225 for (size_t i = 0; i < g_large_message_size; i += 1024) { | |
| 1226 c += static_cast<char*>(addr)[i]; | |
| 1227 } | |
| 1228 EXPECT_GE(GetResidentSize(), | |
| 1229 globals->initial_resident_size + g_large_message_size); | |
| 1230 | |
| 1231 shared_memory.reset(); | |
| 1232 EXPECT_LE(GetResidentSize(), | |
| 1233 globals->initial_resident_size + g_expected_memory_increase); | |
| 1234 | |
| 1235 SendControlMessage(sender, true); | |
| 1236 } | |
| 1237 | |
| 1238 MULTIPROCESS_IPC_TEST_CLIENT_MAIN(MemoryUsageLargeMessage) { | |
| 1239 return CommonPrivilegedProcessMain(&MemoryUsageLargeMessageCallback, | |
| 1240 "MemoryUsageLargeMessage"); | |
| 1241 } | |
| 1242 | |
| 1243 // Tests the memory usage characteristics of attachment brokering many small | |
| 1244 // messages. This test has the *potential* to be flaky, since it compares | |
| 1245 // resident memory at different points in time, and that measurement is | |
| 1246 // non-deterministic. | |
| 1247 TEST_F(IPCAttachmentBrokerMacTest, MemoryUsageManyMessages) { | |
| 1248 // Mach-based SharedMemory isn't support on OSX 10.6. | |
| 1249 if (base::mac::IsOSSnowLeopard()) | |
| 1250 return; | |
| 1251 | |
| 1252 CommonSetUp("MemoryUsageManyMessages"); | |
| 1253 | |
| 1254 for (int i = 0; i < g_large_message_count; ++i) { | |
| 1255 std::string message = base::IntToString(i); | |
| 1256 message += '\0'; | |
| 1257 size_t end = message.size(); | |
| 1258 message.resize(g_medium_message_size); | |
| 1259 std::fill(message.begin() + end, message.end(), 'a'); | |
| 1260 SendMessage1(message); | |
| 1261 | |
| 1262 base::MessageLoop::current()->RunUntilIdle(); | |
| 1263 } | |
| 1264 | |
| 1265 if (get_result_listener()->get_result() == RESULT_UNKNOWN) | |
| 1266 base::MessageLoop::current()->Run(); | |
| 1267 | |
| 1268 CommonTearDown(); | |
| 1269 } | |
| 1270 | |
| 1271 void MemoryUsageManyMessagesCallback(IPC::Sender* sender, | |
| 1272 const IPC::Message& message, | |
| 1273 ChildProcessGlobals* globals) { | |
| 1274 // Volatile prevents the compiler from optimizing out |c|. | |
| 1275 static volatile char c = '\0'; | |
| 1276 static int message_index = 0; | |
| 1277 | |
| 1278 { | |
| 1279 // Map the shared memory, and make sure that its pages are counting towards | |
| 1280 // resident size. | |
| 1281 base::SharedMemoryHandle shm(GetSharedMemoryHandleFromMsg1(message)); | |
| 1282 scoped_ptr<base::SharedMemory> shared_memory( | |
| 1283 MapSharedMemoryHandle(shm, false)); | |
| 1284 | |
| 1285 char* addr = static_cast<char*>(shared_memory->memory()); | |
| 1286 std::string message_string(addr); | |
| 1287 int message_int; | |
| 1288 ASSERT_TRUE(base::StringToInt(message_string, &message_int)); | |
| 1289 ASSERT_EQ(message_index, message_int); | |
| 1290 for (size_t i = 0; i < g_medium_message_size; i += 1024) { | |
| 1291 c += static_cast<char*>(addr)[i]; | |
| 1292 } | |
| 1293 } | |
| 1294 | |
| 1295 ++message_index; | |
| 1296 | |
| 1297 if (message_index == 1) { | |
| 1298 // Disable message logging, since it significantly contributes towards total | |
| 1299 // memory usage. | |
| 1300 LOG(INFO) << "Disable privileged process message logging."; | |
| 1301 globals->message_logging = false; | |
| 1302 } | |
| 1303 | |
| 1304 if (message_index == g_large_message_count) { | |
| 1305 size_t memory_increase_kb = | |
| 1306 (GetResidentSize() - globals->initial_resident_size) / 1024; | |
| 1307 LOG(INFO) << "Increase in memory usage in KB: " << memory_increase_kb; | |
| 1308 | |
| 1309 // The total increase in resident size should be less than 1MB. The exact | |
| 1310 // amount is not deterministic. | |
| 1311 bool success = memory_increase_kb < 1024; | |
| 1312 SendControlMessage(sender, success); | |
| 1313 } | |
| 1314 } | |
| 1315 | |
| 1316 MULTIPROCESS_IPC_TEST_CLIENT_MAIN(MemoryUsageManyMessages) { | |
| 1317 return CommonPrivilegedProcessMain(&MemoryUsageManyMessagesCallback, | |
| 1318 "MemoryUsageManyMessages"); | |
| 1319 } | |
| 1320 | |
| 1155 } // namespace | 1321 } // namespace |
| OLD | NEW |