Pegar dados especificos de do /proc/stat

1. Pegar dados especificos de do /proc/stat

William Lima
willdoidao

(usa Ubuntu)

Enviado em 09/09/2014 - 09:37h

Pessoal,

Como faço para caprturar dados sepadorados de uma linha de arquivo, tipo se der um cat /proc/stat obtenho em uma das linhas o seguinte retorno:

 cpu0 96804 9119 387122 6739554 62390 456399 64543 0 0 0  


Fiz um script bem ralé só para testar, pretendo melhorar bem o código e otimizar mas primeiro preciso fazer funcionar.
Já consegui fazer o levantamento da quantidade, setar a linha que contém
cpu0, mas não sei como dividir esses números na frente dela em variavés.

Segue meu rascunho de código:


#include <iostream>
#include <fstream>
#include <cstring>
#include <stdlib.h>
#include <sstream>
#include <fcntl.h>
#include <stdint.h>


//Variaveis
FILE *fp;
int line_num =1;
int find_result =0;
char buffer[8192];


int Pesquisa()
{

if((fp = fopen("/proc/stat","r")) != NULL)
{
while(fgets(buffer,8192,fp) != NULL)
{
if((strstr(buffer,"cpu"))!= NULL)
{
find_result++;
}
line_num++;
}
if(fp)
fclose(fp);
return find_result;
}
else{
return -1;
}
}

void cpus_valores(int total)
{
int stat,useVmstatfile,cpu;
std::string Testes;

cpu = total -1;

std::ostringstream ss;

if((fp = fopen("/proc/stat","r")) != NULL)
{
for(int i = 0; i< cpu; i++)
{
ss << "cpu" << i;
Testes = ss.str();
//Teste de Captura do Arquivo
while(fgets(buffer,8192,fp) != NULL)
{
if((strstr(buffer,"Testes"))!= NULL)
{



}
line_num++;
}
ss.str("");
}
if(fp)
fclose(fp);

}

}



int main(int argc, char *argv[])
{
int Total = 0;

Total = Pesquisa();
std::cout<< Total << std::endl;
cpus_valores(Total);


return 0;
}



  


2. Re: Pegar dados especificos de do /proc/stat

euteste da silva
foxbit3r

(usa Solaris)

Enviado em 09/09/2014 - 09:49h

Resp.: strtok, sscanf e outros...
Dà uma olha nas funçoes da biblioteca string.h, vc encontra muitas coisas dela no google.


3. Re: Pegar dados especificos de do /proc/stat

Paulo
paulo1205

(usa Ubuntu)

Enviado em 09/09/2014 - 13:04h

willdoidao escreveu:

Pessoal,

Como faço para caprturar dados sepadorados de uma linha de arquivo, tipo se der um cat /proc/stat obtenho em uma das linhas o seguinte retorno:

 cpu0 96804 9119 387122 6739554 62390 456399 64543 0 0 0  


Fiz um script bem ralé só para testar, pretendo melhorar bem o código e otimizar mas primeiro preciso fazer funcionar.
Já consegui fazer o levantamento da quantidade, setar a linha que contém
cpu0, mas não sei como dividir esses números na frente dela em variavés.


Como você está usando C++, pode usar std::getline() (dentro de <string>) para ler as linhas, e depois colocar essa linha dentro de um std::istringstring, para enfim separar os campos através de usos sucessivos de std::istream::operator>>() (uso semelhante a quando você lê de std::cin, por exemplo). Pode também aplicar sobre esse std::istringstream um loop com std::getline(), usando espaços (e não '\n', que é o default) como separador. Pode, ainda, descer um pouco mais o nível, e usar std::string::find(), std::string::substr() e outras funções relacionadas.

Segue um exemplo usando C++11.

#include <iostream>
#include <fstream>
#include <sstream>
#include <iomanip>
#include <string>
#include <map>
#include <vector>

using namespace std;

// Largura das colunas na hora de imprimir os dados lidos.
const unsigned col_width=11u;

int main(){
// Vetor com os titulos das colunas. Os nomes das colunas são
// passados para um construtor que utiliza a nova sintaxe do
// C++11, que aceita uma lista de elementos.
vector<string> col_names{
"CPU", "user", "nice", "system", "idle", "iowait",
"irq", "softirq", "stolen", "guest", "guest_nice"
};

// cpu_data faz o mapeamento entre o nome de uma CPU (string) e
// uma lista de contadores (vetor de inteiros sem sinal). Eu
// usei um vetor de valores sem nomes predefinidos porque cada
// versão de kernel pode ter mais ou menos campos do que outras
// versões (sendo que as primeiras quatro ou cinco colunas pa-
// recem ter "sempre" existido). Mas como este programa monta
// uma tabela, eu acabei usando o vetor col_names, definido acima,
// com os nomes de colunas que vi numa manpage.
map<string, vector<unsigned long long>> cpu_data;

// Uma variável inteira sem sinal, que será usada no loop de
// leitura dos sucessivos contadores associados a cada CPU.
unsigned long long counter;

// Um buffer para armazenar cada linha lida.
string line;

// Depois de ler cada linha, eu uso um "input string stream"
// (istringstream, definido em <sstream>) para separar as colunas.
istringstream line_parser;

// Tente descobri qual informação a variável abaixo armazena. ;)
string cpu_name;

// Declara um stream de entrada para ler do arquivo, e já define
// de que arquivo será feita a leitura.
ifstream proc_stat("/proc/stat");

// Lê uma linha do arquivo de entrada. Se a leitura for bem sucedida,
// entra no corpo do while.
while(getline(proc_stat, line)){
// Limpa do objeto separador eventuais sinalizações de erro e/ou
// fim de dados, que podem ter permanecido da iteração anterior do
// loop. Em seguida, alimenta o separador com a linha que acabou
// de ser lida, para que possamos separar suas partes.
line_parser.clear();
line_parser.str(line);

// A primeira coluna da linha é o nome da entidade cujos contadores
// estarão nas colunas seguintes. Como só temos interesse nos con-
// tadores de CPUs, só se entra no corpo do if se essa primeira co-
// luna lida tiver realmente os três primeiros caracteres (i.e. a
// substring começando na posição 0 e com 3 caracteres de compri-
// mento) iguais ao texto "cpu".
if((line_parser >> cpu_name) && cpu_name.substr(0, 3)=="cpu"){
// Existem dois tipo de linha com informações de CPU: as que são
// relativas a cada core, que aparecem com a string "cpu" seguida
// de um número, e a do total consolidado, que aparece apenas como
// "cpu", sem sufixo. Para deixar mais claro, este programa troca
// o rótulo da linha consolidada pela string "TOTAL", usando as
// letras maiúsculas para dar ainda mais destaque à diferença.
if(cpu_name=="cpu")
cpu_name="TOTAL";

// Loop de leitura dos contadores. Cada contador é lido do sepa-
// rador de colunas para a variável counter. Se a leitura for bem
// sucedida, esse valor é colocado ao final do vetor de contadores
// associados à CPU indicada por cpu_name.
while(line_parser >> counter)
cpu_data[cpu_name].push_back(counter);
}
}

// Fecha o arquivo de onde os dados foram lidos.
proc_stat.close();

// Imprime os dados lidos, começando com os títulos das colunas (lem-
// brando que setw() é um manipulador que informa a largura usada para
// o próximo dado a ser impresso)...
for(auto &col : col_names) // itera sobre os nomes das colunas.
cout << setw(col_width) << col;
cout << '\n';

// ... e terminando com a impressão dos dados em si.
for(auto &cpu_info : cpu_data){ // itera sobre os registros do mapa.
// Primeiro imprime o nome da CPU (primeiro campo do registro, que
// é usado no momento da leitura como chave de indexação do mapa)...
cout << setw(col_width) << cpu_info.first;

// ... e depois o valor de cada contador do vetor de contadores (o
// vetor é o segundo campo do registro do mapa).
for(auto &col : cpu_info.second)
cout << setw(col_width) << col;

// Fim dos dados desta CPU: pula a linha.
cout << '\n';
}
}



4. Re: Pegar dados especificos de do /proc/stat

Luís Fernando C. Cavalheiro
lcavalheiro

(usa Slackware)

Enviado em 09/09/2014 - 14:15h

willdoidao escreveu:

Pessoal,

Como faço para caprturar dados sepadorados de uma linha de arquivo, tipo se der um cat /proc/stat obtenho em uma das linhas o seguinte retorno:

 cpu0 96804 9119 387122 6739554 62390 456399 64543 0 0 0  


Fiz um script bem ralé só para testar, pretendo melhorar bem o código e otimizar mas primeiro preciso fazer funcionar.
Já consegui fazer o levantamento da quantidade, setar a linha que contém
cpu0, mas não sei como dividir esses números na frente dela em variavés.

Segue meu rascunho de código:


#include <iostream>
#include <fstream>
#include <cstring>
#include <stdlib.h>
#include <sstream>
#include <fcntl.h>
#include <stdint.h>


//Variaveis
FILE *fp;
int line_num =1;
int find_result =0;
char buffer[8192];


int Pesquisa()
{

if((fp = fopen("/proc/stat","r")) != NULL)
{
while(fgets(buffer,8192,fp) != NULL)
{
if((strstr(buffer,"cpu"))!= NULL)
{
find_result++;
}
line_num++;
}
if(fp)
fclose(fp);
return find_result;
}
else{
return -1;
}
}

void cpus_valores(int total)
{
int stat,useVmstatfile,cpu;
std::string Testes;

cpu = total -1;

std::ostringstream ss;

if((fp = fopen("/proc/stat","r")) != NULL)
{
for(int i = 0; i< cpu; i++)
{
ss << "cpu" << i;
Testes = ss.str();
//Teste de Captura do Arquivo
while(fgets(buffer,8192,fp) != NULL)
{
if((strstr(buffer,"Testes"))!= NULL)
{



}
line_num++;
}
ss.str("");
}
if(fp)
fclose(fp);

}

}



int main(int argc, char *argv[])
{
int Total = 0;

Total = Pesquisa();
std::cout<< Total << std::endl;
cpus_valores(Total);


return 0;
}


Admiro muito o seu trabalho em bolar algo em C, mas só por curiosidade, usar cat, grep, cut e paste não lhe passou pela cabeça?

EDIT: usando um exemplo tosco com esse caso do /proc/stat,

 $ cat /proc/stat | grep cpu0 | sed -e "s/\ /\n/g" 


Isso vai organizar aquela linha que você me deu em uma coluna, vai ficar assim:
cpu0
96804
9119
387122
6739554
62390
456399
64543
0
0
0

Pulo do gato: crie um arquivo que contenha o nome das variáveis em colunas, assim:
VAR1
VAR2
VAR3
VAR4
VAR5
VAR6
VAR7
VAR8
VAR9
VAR0
VARA


Agora, o como faz. Vamos chamar o arquivo com o nome das variáveis de variaveis.txt. Rode os comandos:
 $ cat /proc/stat | grep cpu0 | sed -e "s/\ /\n/g" >> dados.txt
$ paste -d"=" variaveis.txt dados.txt >> source.txt

O conteúdo de source.txt vai ser
VAR1=cpu0
VAR2=96804
VAR3=9119
VAR4=387122
VAR5=6739554
VAR6=62390
VAR7=456399
VAR8=64543
VAR9=0
VAR0=0
VARA=0

Em um shellscript, você pode usar a linha
 . source.txt 

Pra chamar todas essas variáveis


5. Re: Pegar dados especificos de do /proc/stat

William Lima
willdoidao

(usa Ubuntu)

Enviado em 09/09/2014 - 14:32h

Caras agradeço pela ajuda, estou perto de resolver a parada, mas ainda preciso de uma ajuda, orientação a objeto ainda está me tirando umas noites de sono.

O que fiz após a dica do foxbit3r fiz o seguinte:

no cpu.h criei um objeto:

class Valores{
public:
std::string cuse;
std::string cice;
std::string csys;
std::string cide;
std::string cwio;
};

o vou postar somente a classe que estou trabalhando:


void cpus_valores(int total)
{
std::ostringstream ss;
std::string Nome_cpu;
char buffe[255];

total = total-1;

for(int i =0; i<=total;i++)
{
//Montando a string de pesquisa
ss << "cpu" << i;
Nome_cpu = ss.str();

FILE *arquivo = fopen("/proc/stat","r");
char buffe[255];
if(arquivo !=NULL)
{
while(fgets(buffe,255,arquivo))
{
if((strstr(buffe,Nome_cpu.c_str()))!=NULL)
{

Valores *cpu = new Valores();

std::sscanf(buffe, "%s %s %s %s %s",cpu->cuse.c_str(),cpu->cice.c_str(),cpu->csys.c_str(),cpu->cide.c_str(),cpu->cwio.c_str());

std::cout << i <<":"
<< cpu->cuse.c_str()<< " "
<< cpu->cice.c_str()<< " "
<< cpu->csys.c_str()<< " "
<< cpu->cide.c_str()<< " "
<< cpu->cwio.c_str()<< " "
<< std::endl;
}
}
fclose(arquivo);
}
else
std::cout<<"falha a abrir arquivo" << std::endl;

ss.str("");
}
}


A saida que está dando é:


0:67176 67176 67176 67176 67176
1:67175 67175 67175 67175 67175
2:67174 67174 67174 67174 67174
3:67173 67173 67173 67173 67173


Sendo que o correto seria:

49998 51 45304 67176 24479
49997 50 45303 67175 24478
49996 49 45302 67174 24477
49995 48 45301 67173 24466


Poderia me mostrar meu erro?


6. Re: Pegar dados especificos de do /proc/stat

Paulo
paulo1205

(usa Ubuntu)

Enviado em 09/09/2014 - 14:44h

std::string::c_str() lhe devolve uma referência ao conteúdo do objeto std::string que não deve ser usada para fazer modificações nesse objeto. Você nem mesmo tem garantias de que se trata do próprio objeto, e não somente de uma cópia. Note, inclusive, que o ponteiro devolvido pela função é designado como const char *. Ao usá-lo como argumento de sscanf(), você tacitamente descarta o const.

Você chegou a olhar a mensagem que enviei acima? Acho que dali você pode obter a informação de que precisa. Se não tiver entendido o código, pode perguntar, que eu explico.


7. Re: Pegar dados especificos de do /proc/stat

William Lima
willdoidao

(usa Ubuntu)

Enviado em 09/09/2014 - 15:21h

paulo1205

Cheguei até rodar o seu exemplo e funciona legal, mas como falei antes estou aprendendo orientação a objeto, não consegui entender quase nada do seu exemplo, teria como colocar uns comentários ali?


8. Re: Pegar dados especificos de do /proc/stat

Paulo
paulo1205

(usa Ubuntu)

Enviado em 09/09/2014 - 17:54h

Acabei de confirmar o preceito de que comentar demais é uma boa maneira de transformar um programa elegante e de poucas linhas num monstro. ;) De todo modo, editei minha primeira postagem com esses comentários. Espero que lhe sejam úteis.

Tenho a impressão de que o problema que você enfrenta é menos com OO e mais com conhecer as ferramentas e a própria sintaxe da linguagem. Acho que, se você tivesse lido sobre std::istringstream ou sobre a nova forma do loop for que automaticamente itera sobre os valores de arrays ou containers da biblioteca padrão do C++, você provavelmente teria entendido o programa não-comentado logo de primeira, até porque os nomes das variáveis indicam claramente seus papéis no programa.


9. Re: Pegar dados especificos de do /proc/stat

Paulo
paulo1205

(usa Ubuntu)

Enviado em 09/09/2014 - 18:21h

Pelo que me pareceu, o seu programa deveria ser muito parecido com o meu, com a diferença de que em lugar de ler para um vetor de contadores genéricos, você quer ler algumas das colunas para uma estrutura com campos que têm nomes bem conhecidos.

Em C++, você trocaria (simplificando o programa anterior)

map<string, vector<unsigned long long>> cpu_data;

/* ... */

if((line_parser >> cpu_name) && cpu_name.substr(0, 3)=="cpu"){
while(line_parser >> counter)
cpu_data[cpu_name].push_back(counter);
}


por algo como vai abaixo (também simplificado: você talvez queira tratar possíveis erros de leitura, com os quais não estou me preocupando neste exemplo).

struct exemplo {
unsigned long long user, system, idle, iowait; // Nomes de exemplo!!!
};

map<string, exemplo> cpu_data;

/* ... */

if((line_parser >> cpu_name) && cpu_name.substr(0, 3)=="cpu"){
// Note que é uma linha de código só, que eu quebrei para ficar mais legível.
line_parser
>> cpu_data[cpu_name].user
>> cpu_data[cpu_name].system
>> cpu_data[cpu_name].idle
>> cpu_data[cpu_name].iowait
;
}