// processinfo_linux2.cpp

/*    Copyright 2009 10gen Inc.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

#include "processinfo.h"

#include <iostream>
#include <stdio.h>
#include <malloc.h>
#include <db/jsobj.h>
#include <unistd.h>
#include <sys/mman.h>

using namespace std;

#define KLONG long
#define KLF "l"

namespace mongo {

    class LinuxProc {
    public:
        LinuxProc( pid_t pid = getpid() ) {
            char name[128];
            sprintf( name , "/proc/%d/stat"  , pid );

            FILE * f = fopen( name , "r");
            if ( ! f ) {
                stringstream ss;
                ss << "couldn't open [" << name << "] " << errnoWithDescription();
                string s = ss.str();
                // help the assert# control uasserted( 13538 , s.c_str() );
                msgassertedNoTrace( 13538 , s.c_str() );
            }
            int found = fscanf(f,
                               "%d %s %c "
                               "%d %d %d %d %d "
                               "%lu %lu %lu %lu %lu "
                               "%lu %lu %ld %ld "  /* utime stime cutime cstime */
                               "%ld %ld "
                               "%ld "
                               "%ld "
                               "%lu "  /* start_time */
                               "%lu "
                               "%ld " // rss
                               "%lu %"KLF"u %"KLF"u %"KLF"u %"KLF"u %"KLF"u "
                               /*
                                 "%*s %*s %*s %*s "
                                 "%"KLF"u %*lu %*lu "
                                 "%d %d "
                                 "%lu %lu"
                               */

                               ,

                               &_pid,
                               _comm,
                               &_state,
                               &_ppid, &_pgrp, &_session, &_tty, &_tpgid,
                               &_flags, &_min_flt, &_cmin_flt, &_maj_flt, &_cmaj_flt,
                               &_utime, &_stime, &_cutime, &_cstime,
                               &_priority, &_nice,
                               &_alarm,
                               &_nlwp,
                               &_start_time,
                               &_vsize,
                               &_rss,
                               &_rss_rlim, &_start_code, &_end_code, &_start_stack, &_kstk_esp, &_kstk_eip

                               /*
                                 &_wchan,
                                 &_exit_signal, &_processor,
                                 &_rtprio, &_sched
                               */
                              );
            if ( found == 0 ) {
                cout << "system error: reading proc info" << endl;
            }
            fclose( f );
        }

        unsigned long getVirtualMemorySize() {
            return _vsize;
        }

        unsigned long getResidentSize() {
            return (unsigned long)_rss * 4 * 1024;
        }

        int _pid;
        // The process ID.

        char _comm[128];
        // The filename of the executable, in parentheses.  This is visible whether or not the executable is swapped out.

        char _state;
        //One character from the string "RSDZTW" where R is running, S is sleeping in an interruptible wait, D is waiting  in  uninterruptible
        //  disk sleep, Z is zombie, T is traced or stopped (on a signal), and W is paging.

        int _ppid;
        // The PID of the parent.

        int _pgrp;
        // The process group ID of the process.

        int _session;
        // The session ID of the process.

        int _tty;
        // The tty the process uses.

        int _tpgid;
        // The process group ID of the process which currently owns the tty that the process is connected to.

        unsigned long _flags; // %lu
        // The  kernel flags word of the process. For bit meanings, see the PF_* defines in <linux/sched.h>.  Details depend on the kernel version.

        unsigned long _min_flt; // %lu
        // The number of minor faults the process has made which have not required loading a memory page from disk.

        unsigned long _cmin_flt; // %lu
        // The number of minor faults that the process

        unsigned long _maj_flt; // %lu
        // The number of major faults the process has made which have required loading a memory page from disk.

        unsigned long _cmaj_flt; // %lu
        // The number of major faults that the process

        unsigned long _utime; // %lu
        // The number of jiffies that this process has been scheduled in user mode.

        unsigned long _stime; //  %lu
        // The number of jiffies that this process has been scheduled in kernel mode.

        long _cutime; // %ld
        // The number of jiffies that this removed field.

        long _cstime; // %ld

        long _priority;
        long _nice;

        long _nlwp; // %ld
        // The time in jiffies before the next SIGALRM is sent to the process due to an interval timer.

        unsigned long _alarm;

        unsigned long _start_time; // %lu
        // The time in jiffies the process started after system boot.

        unsigned long _vsize; // %lu
        // Virtual memory size in bytes.

        long _rss; // %ld
        // Resident Set Size: number of pages the process has in real memory, minus 3 for administrative purposes. This is just the pages which
        // count  towards  text,  data, or stack space.  This does not include pages which have not been demand-loaded in, or which are swapped out

        unsigned long _rss_rlim; // %lu
        // Current limit in bytes on the rss of the process (usually 4294967295 on i386).

        unsigned long _start_code; // %lu
        // The address above which program text can run.

        unsigned long _end_code; // %lu
        // The address below which program text can run.

        unsigned long _start_stack; // %lu
        // The address of the start of the stack.

        unsigned long _kstk_esp; // %lu
        // The current value of esp (stack pointer), as found in the kernel stack page for the process.

        unsigned long _kstk_eip; // %lu
        // The current EIP (instruction pointer).



    };


    ProcessInfo::ProcessInfo( pid_t pid ) : _pid( pid ) {
    }

    ProcessInfo::~ProcessInfo() {
    }

    bool ProcessInfo::supported() {
        return true;
    }

    int ProcessInfo::getVirtualMemorySize() {
        LinuxProc p(_pid);
        return (int)( p.getVirtualMemorySize() / ( 1024.0 * 1024 ) );
    }

    int ProcessInfo::getResidentSize() {
        LinuxProc p(_pid);
        return (int)( p.getResidentSize() / ( 1024.0 * 1024 ) );
    }

    void ProcessInfo::getExtraInfo(BSONObjBuilder& info) {
        struct mallinfo malloc_info = mallinfo(); // structure has same name as function that returns it. (see malloc.h)
        info.append("heap_usage_bytes", malloc_info.uordblks);

        LinuxProc p(_pid);
        info.append("page_faults", (int)p._maj_flt);
    }

    bool ProcessInfo::blockCheckSupported() {
        return true;
    }

    bool ProcessInfo::blockInMemory( char * start ) {
        static long pageSize = 0;
        if ( pageSize == 0 ) {
            pageSize = sysconf( _SC_PAGESIZE );
        }
        start = start - ( (unsigned long long)start % pageSize );
        unsigned char x = 0;
        if ( mincore( start , 128 , &x ) ) {
            log() << "mincore failed: " << errnoWithDescription() << endl;
            return 1;
        }
        return x & 0x1;
    }


}
