Source

pypy-c4gc / azm_mem_test / test.c

Full commit
/*
 * Copyright (c) 2012, Greg Bowyer. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.
 *
 * This code 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
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * This file contains work derived from Azul and Oracle, using code liberally
 * copy and pasted from the openJDK project and the managed runtime initiative
 * project
 *
 */
#include <stdio.h>
#include <string.h>

#include "utils.h"

#include <aznix/az_memory.h>
#include <aznix/az_pgroup.h>

#include <unistd.h>
#include <stdlib.h>

/**
 * The following code serves as an exampler for how to use the azul
 * MRI memory interfaces from a naive C perspective, this is done
 * to get a handle on how these interfaces operate within hotspot
 * in a simplified fashion
 *
 * From here it should be easier to see how these interfaces work
 * within hotspot and as such translate the base ideas into pypy
 */

#define GIGA(x)	(x*1024*1024*1024LL)
#define MEGA(x)	(x*1024*1024LL)

typedef void *address_t;                // User address space pointer

// The following are java types nonsence {{{
const int LogBytesPerShort   = 1;
const int LogBytesPerInt     = 2;
#ifdef _LP64
const int LogBytesPerWord    = 3;
#else
const int LogBytesPerWord    = 2;
#endif
const int LogBytesPerLong    = 3;

// Define various constants for virtual memory page sizes.  Having compiler
// accessible constants is beneficial for fast code in GPGC.
//
// TODO - even if this is a contrieved example, we should be getting these from getpagesize()
// I can think of at least one x86 processor where largepagesize == 4Mb not 2
const long     LogBytesPerSmallPage = 12;  // 2^12 = 4 KB page size
const long     LogWordsPerSmallPage = LogBytesPerSmallPage - LogBytesPerWord;
const intptr_t BytesPerSmallPage    = 1 << LogBytesPerSmallPage;
const intptr_t WordsPerSmallPage    = 1 << LogWordsPerSmallPage;

const long     LogBytesPerLargePage = 21;  // 2^21 = 2 MB page size
const long     LogWordsPerLargePage = LogBytesPerLargePage - LogBytesPerWord;
const intptr_t BytesPerLargePage    = 1 << LogBytesPerLargePage;
const intptr_t WordsPerLargePage    = 1 << LogWordsPerLargePage;

// }}}

enum MemoryAccount {
    CHEAP_COMMITTED_MEMORY_ACCOUNT = AZMM_DEFAULT_ACCOUNT, // 0
    // Rename the JHeap to make it obvious where this account belongs
    PY_COMMITTED_MEMORY_ACCOUNT    = AZMM_JHEAP_ACCOUNT,   // 2
    PY_PAUSE_MEMORY_ACCOUNT        = AZMM_GCPP_ACCOUNT,    // 3
    nof_MEMORY_ACCOUNTS,
    ALL_ACCOUNTS                   = AZMM_NR_MEMORY_ACCOUNTS
};

void os_setup_avm_launch_env() {

    unsigned long avm_process_id = (unsigned long) getpid();
    unsigned long avm_mem_max    = (unsigned long) 1000L;
    unsigned long avm_mem_commit = (unsigned long) 500L;

    printf("Process id %d\n", avm_process_id);

    // 1. Nice easy call, check is /dev/azul exists (I assume this 
    // would also check for the proxied variety of Azul MRI)
    //
    int retcode = az_pmem_subsys_available();
    assertfalse(retcode, "The Azul subsytem was not available, no Kern module loaded ? check permissions on /dev/azul\n");

    // 2. We setup ‟funds” for the default ‟account”
    retcode = az_pmem_set_account_funds(avm_process_id, CHEAP_COMMITTED_MEMORY_ACCOUNT,
            AZMM_COMMITTED_FUND, AZMM_OVERDRAFT_FUND);

    if (retcode) {
        printf("FUCK! [%lu] failed to set Account [%d] with funds %d\%d: %s\n",
                avm_process_id, CHEAP_COMMITTED_MEMORY_ACCOUNT, AZMM_COMMITTED_FUND, 
                AZMM_OVERDRAFT_FUND, strerror(errno));
        abort();
    }
    
    // 3. Next setup ‟funds” for the default JVM Heap (or in our case
    // the python / pypy objspace)
    retcode = az_pmem_set_account_funds(avm_process_id, PY_COMMITTED_MEMORY_ACCOUNT,
                                    AZMM_COMMITTED_FUND, AZMM_OVERDRAFT_FUND);
    if (retcode) {
      printf("[%lu] Failed to set AC# %d with funds %d/%d: %s\n",
               avm_process_id,
               PY_COMMITTED_MEMORY_ACCOUNT, AZMM_COMMITTED_FUND, AZMM_OVERDRAFT_FUND,
               strerror(errno));
        abort();
    }

    // 4. I *think* this is where funds are established in order to allow
    // GPGC to operate pauseless in all but the most pathalogical of
    // corner cases, this account is, if my reading of the code is
    // correct, the place where the "we can collect the heap with only
    // one free page" claim comes from
    retcode = az_pmem_set_account_funds(avm_process_id, PY_PAUSE_MEMORY_ACCOUNT,
                                    AZMM_GCPP_FUND, AZMM_GCPP_FUND);
    if (retcode) {
      printf("[%lu] Failed to set AC# %d with funds %d/%d: %s\n",
               avm_process_id,
               PY_PAUSE_MEMORY_ACCOUNT, AZMM_GCPP_FUND, AZMM_GCPP_FUND,
               strerror(errno));
        abort();
    }
    printf("[%lu] Associated accts with funds\n", avm_process_id);

    // 5. Actually fund the CHeap account, we know what his overdraft limit is, now put some credit into his account
    retcode = az_pmem_fund_account(avm_process_id, CHEAP_COMMITTED_MEMORY_ACCOUNT, avm_mem_commit);
    if (retcode) {
      printf("[%lu] Failed to fund AC# %d with %lu: %s\n",
               avm_process_id, CHEAP_COMMITTED_MEMORY_ACCOUNT, avm_mem_commit,
               strerror(errno));
        abort();
    }
    printf("[%lu] Funded AC# %d with %lu\n",
           avm_process_id, AZMM_DEFAULT_ACCOUNT, avm_mem_commit);

    // 6. We now establish the maximum amount of memory that MRI
    // can lay claim to, this is in essence the only real user
    // tunable here
    retcode = az_pmem_set_maximum(avm_process_id, avm_mem_max);
    if (retcode) {
      printf("[%lu] Failed to set mem_rlimit with %lu: %s\n",
               avm_process_id, avm_mem_max,
               strerror(errno));
        abort();
    }
    printf("[%lu] Set mem_rlimit with %lu\n", avm_process_id, avm_mem_max);

    // 7. Set the maximum amount of memory that the default account can lay claim to
    retcode = az_pmem_set_account_maximum(avm_process_id, AZMM_DEFAULT_ACCOUNT, avm_mem_max);
    if (retcode) {
      printf("[%lu] Failed to set AC# 0 mem_rlimit with %lu: %s\n",
               avm_process_id, avm_mem_max,
               strerror(errno));
        abort();
    }
    printf("[%lu] Set AC# 0 mem_rlimit with %lu\n",
           avm_process_id, avm_mem_max);


    // Reserve low memory upfront for VM structures
    //int flags = AZMM_BATCHABLE;
    /*
    size_t len = __VM_STRUCTURES_END_ADDR__ - __VM_STRUCTURES_START_ADDR__; 
    retcode = az_mreserve((address_t)__VM_STRUCTURES_START_ADDR__, len, flags);
    if (retcode < 0) {
      printf("az_mreserve(VMSTRUCTS) failed: %s", strerror(errno));
    }
    */

}

char* azmalloc(size_t bytes, void* preferred_addr, bool must_allocate_here, 
        bool batchable, bool aliasable) {
	address_t addr = (address_t) preferred_addr;
	
	assert((size_t)addr % getpagesize() == 0, "reserve_memory on page boundaries");
	assert(bytes % getpagesize() == 0, "reserve_memory in page-sized chunks");
	size_t reserved_size = round_to(bytes, BytesPerLargePage);
	/*
	assert(preferred_addr != NULL && must_allocate_here == true, 
            "need specific addresses for reservations with azmem");
*/

	int flags = batchable ? AZMM_BATCHABLE : 0;
	flags |= aliasable ? AZMM_ALIASABLE : 0;
	if(flags) {
        assert((reserved_size % (GIGA(1))) == 0, 
                "batchable and aliasable memory needs to be in 1GB sizes" );
    }

	int ret = az_mreserve(addr, reserved_size, flags);
	if (ret < 0) {
		printf("reserve_memory failed: %s\n", strerror(errno));
		addr = NULL;
	}
	
	return (char*)addr;
}

int main(int argc, char** argv) {

    printf("First attempt to init some az mem\n");
    os_setup_avm_launch_env();

    // py: len("This is a test") +_ 1 == 15
    // It will throw a hissy fit unless it is asked to reseve
    // in page size lumps, so lets do that

    printf("Example args for cmd#1: 1 0x200000000 0x400000000\n");
    char* wibble = azmalloc(0x400000000, (void*) 0x200000000, false, false, false);   
    assert(wibble != NULL, "Gack memory not reserved\n");

    // Yeah its crummy, its an example alright !!
    wibble[0] = 'T';
    wibble[1] = 'h';
    wibble[2] = 'i';
    wibble[3] = 's';
    wibble[4] = ' ';
    wibble[5] = 'i';
    wibble[6] = 's';
    wibble[7] = ' ';
    wibble[8] = 'a';
    wibble[9] = ' ';
    wibble[10] = 't';
    wibble[11] = 'e';
    wibble[12] = 's';
    wibble[13] = 't';
    wibble[14] = '\0';

    // I hope that azul memory can be used somewhat like a real
    // void* malloc c pointer you would get from good old fashioned
    // malloc/calloc libc and friends
    // else this is going to break rather badly
    printf("Did the azul test return \"This is a test\" ? -> [%s]\n", 
            wibble);

    // Ok so I cant yet free memory, so lets just screw the OS and leak
    // I think my random compile of the kernel side does not free memory
    // quite right anyhow
    //azfree(wibble);

    return 0;
}
/* vim: set et fenc=utf-8 ff=unix sts=4 sw=4 ts=4 foldmethod=marker foldlevel=0: */