You can use getrusage( RUSAGE_CHILDREN, ... ) to obtain the maximum resident memory. Note that this call will return the maximum memory used by the biggest child at that point in time.
In the example below I used boost::process because it gives better control but it's up to you to use std::system or not, works the same way.
#include <string> #include <cstdint> #include <string.h> #include <iostream> #include <boost/process/child.hpp> #include <sys/resource.h> namespace bp = boost::process; int parent( const std::string& exename ) { // Loop from 0 to 10 megabytes for ( int j=0; j<10; ++j ) { // Command name is the name of this executable plus one argument with size std::string gencmd = exename + " " + std::to_string(j); // Start process bp::child child( gencmd ); // Wait for it to allocate memory sleep(1); // Query the memory usage at this point in time struct rusage ru; getrusage( RUSAGE_CHILDREN, &ru ); std::cerr << "Loop:" << j << " mem:"<< ru.ru_maxrss/1024. << " MB" << std::endl; // Wait for process to quit child.wait(); if ( child.exit_code()!=0 ) { std::cerr << "Error executing child:" << child.exit_code() << std::endl; return 1; } } return 0; } int child( int size ) { // Allocated "size" megabites explicitly size_t memsize = size*1024*1024; uint8_t* ptr = (uint8_t*)malloc( memsize ); memset( ptr, size, memsize ); // Wait for the parent to sample our memory usage sleep( 2 ); // Free memory free( ptr ); return 0; } int main( int argc, char* argv[] ) { // Without arguments, it is the parent. // Pass the name of the binary if ( argc==1 ) return parent( argv[0] ); return child( std::atoi( argv[1] ) ); }
It prints
$ ./env_test Loop:0 mem:0 MB Loop:1 mem:3.5625 MB Loop:2 mem:4.01953 MB Loop:3 mem:5.05469 MB Loop:4 mem:6.04688 MB Loop:5 mem:7.05078 MB Loop:6 mem:7.78516 MB Loop:7 mem:8.97266 MB Loop:8 mem:9.82031 MB Loop:9 mem:10.8867 MB
If you cannot use boost libraries, you'd got to work a little more but it is still feasible.
If you just want to know the maximum size ever of your children processes then the following works with std::system:
#include <cstdio> #include <string> #include <iostream> #include <sstream> #include <string.h> #include <unistd.h> #include <sys/resource.h> int main(int argc, char* argv[]) { if (argc > 1) { size_t size = ::atol(argv[1]); size_t memsize = size * 1024 * 1024; void* ptr = ::malloc(memsize); memset(ptr, 0, memsize); ::sleep(2); ::free(ptr); return 0; } for (int j = 0; j < 10; ++j) { std::ostringstream cmd; cmd << argv[0] << " " << j; int res = std::system(cmd.str().c_str()); if (res < 0) { fprintf(stderr, "ERROR system: %s\n", strerror(errno)); break; } struct rusage ru; res = getrusage(RUSAGE_CHILDREN, &ru); size_t maxmem = ru.ru_maxrss; fprintf(stderr, "Loop:%d MaxMem:%ld\n", j, maxmem); } return 0; }
It prints
Loop:0 MaxMem:3552 Loop:1 MaxMem:4192 Loop:2 MaxMem:5148 Loop:3 MaxMem:6228 Loop:4 MaxMem:7364 Loop:5 MaxMem:8456 Loop:6 MaxMem:9120 Loop:7 MaxMem:10188 Loop:8 MaxMem:11324 Loop:9 MaxMem:12256
However if you want to keep track of the memory usage during the child process execution you cannot use std::system(). First, you need to call fork() to spawn a new process and then execv() to execute a bash command.
#include <string> #include <cstdint> #include <string.h> #include <unistd.h> #include <iostream> #include <sys/resource.h> #include <sys/wait.h> #include <fcntl.h> #include <vector> int parent(const std::string& exename) { // Loop from 0 to 10 megabytes for (int j = 0; j < 10; ++j) { // Command name is the name of this executable plus one argument with size std::string gencmd = exename + " " + std::to_string(j); // Start process pid_t pid = fork(); if (pid == 0) { // child const char* args[] = {"/bin/bash", "-c", gencmd.c_str(), (char*)0}; int res = execv("/bin/bash", (char**)args); // Should never return std::cerr << "execv error: " << strerror(errno) << std::endl; return 1; } // parent long maxmem = 0; while (true) { int status; pid_t rid = ::waitpid(pid, &status, WNOHANG); if (rid < 0) { if (errno != ECHILD) { std::cerr << "waitpid:" << strerror(errno) << std::endl; return 2; } break; } if (rid == pid) { if (WIFEXITED(pid)) { break; } } // Wait for it to allocate memory usleep(10000); // Query the memory usage at this point in time struct rusage ru; int res = getrusage(RUSAGE_CHILDREN, &ru); if (res != 0) { if (errno != ECHILD) { std::cerr << "getrusage:" << errno << strerror(errno) << std::endl; } break; } if (maxmem < ru.ru_maxrss) { maxmem = ru.ru_maxrss; } } std::cerr << "Loop:" << j << " mem:" << maxmem / 1024. << " MB" << std::endl; } return 0; } int child(int size) { // Allocated "size" megabites explicitly size_t memsize = size * 1024 * 1024; uint8_t* ptr = (uint8_t*)malloc(memsize); memset(ptr, size, memsize); // Wait for the parent to sample our memory usage sleep(2); // Free memory free(ptr); return 0; } int main(int argc, char* argv[]) { // Without arguments, it is the parent. // Pass the name of the binary if (argc == 1) return parent(argv[0]); return child(std::atoi(argv[1])); }
The result on my machine is:
$ ./fork_test Loop:0 mem:3.22656 MB Loop:1 mem:3.69922 MB Loop:2 mem:4.80859 MB Loop:3 mem:5.92578 MB Loop:4 mem:6.87109 MB Loop:5 mem:8.05469 MB Loop:6 mem:8.77344 MB Loop:7 mem:9.71875 MB Loop:8 mem:10.7422 MB Loop:9 mem:11.6797 MB
There is a video about this post.
#include <bits/stdc++.h>: stackoverflow.com/questions/31816095/…, (2)using namespace std;: stackoverflow.com/questions/1452721/…popen()to run another application and catch the standard output in a stream. man popen