2011-01-18 20:15:24 +03:00
# include <dirent.h>
2012-01-19 20:08:15 +04:00
# include <limits.h>
# include <stdbool.h>
2011-01-18 20:15:24 +03:00
# include <stdlib.h>
# include <stdio.h>
2012-01-19 20:08:15 +04:00
# include <sys/types.h>
# include <sys/stat.h>
# include <unistd.h>
2012-02-08 20:32:52 +04:00
# include "strlist.h"
# include <string.h>
2011-01-18 20:15:24 +03:00
# include "thread_map.h"
/* Skip "." and ".." directories */
static int filter ( const struct dirent * dir )
{
if ( dir - > d_name [ 0 ] = = ' . ' )
return 0 ;
else
return 1 ;
}
struct thread_map * thread_map__new_by_pid ( pid_t pid )
{
struct thread_map * threads ;
char name [ 256 ] ;
int items ;
struct dirent * * namelist = NULL ;
int i ;
sprintf ( name , " /proc/%d/task " , pid ) ;
items = scandir ( name , & namelist , filter , NULL ) ;
if ( items < = 0 )
2012-01-19 20:08:15 +04:00
return NULL ;
2011-01-18 20:15:24 +03:00
threads = malloc ( sizeof ( * threads ) + sizeof ( pid_t ) * items ) ;
if ( threads ! = NULL ) {
for ( i = 0 ; i < items ; i + + )
threads - > map [ i ] = atoi ( namelist [ i ] - > d_name ) ;
threads - > nr = items ;
}
for ( i = 0 ; i < items ; i + + )
free ( namelist [ i ] ) ;
free ( namelist ) ;
return threads ;
}
struct thread_map * thread_map__new_by_tid ( pid_t tid )
{
struct thread_map * threads = malloc ( sizeof ( * threads ) + sizeof ( pid_t ) ) ;
if ( threads ! = NULL ) {
threads - > map [ 0 ] = tid ;
threads - > nr = 1 ;
}
return threads ;
}
2012-01-19 20:08:15 +04:00
struct thread_map * thread_map__new_by_uid ( uid_t uid )
{
DIR * proc ;
int max_threads = 32 , items , i ;
char path [ 256 ] ;
struct dirent dirent , * next , * * namelist = NULL ;
struct thread_map * threads = malloc ( sizeof ( * threads ) +
max_threads * sizeof ( pid_t ) ) ;
if ( threads = = NULL )
goto out ;
proc = opendir ( " /proc " ) ;
if ( proc = = NULL )
goto out_free_threads ;
threads - > nr = 0 ;
while ( ! readdir_r ( proc , & dirent , & next ) & & next ) {
char * end ;
bool grow = false ;
struct stat st ;
pid_t pid = strtol ( dirent . d_name , & end , 10 ) ;
if ( * end ) /* only interested in proper numerical dirents */
continue ;
snprintf ( path , sizeof ( path ) , " /proc/%s " , dirent . d_name ) ;
if ( stat ( path , & st ) ! = 0 )
continue ;
if ( st . st_uid ! = uid )
continue ;
snprintf ( path , sizeof ( path ) , " /proc/%d/task " , pid ) ;
items = scandir ( path , & namelist , filter , NULL ) ;
if ( items < = 0 )
goto out_free_closedir ;
while ( threads - > nr + items > = max_threads ) {
max_threads * = 2 ;
grow = true ;
}
if ( grow ) {
struct thread_map * tmp ;
tmp = realloc ( threads , ( sizeof ( * threads ) +
max_threads * sizeof ( pid_t ) ) ) ;
if ( tmp = = NULL )
goto out_free_namelist ;
threads = tmp ;
}
for ( i = 0 ; i < items ; i + + )
threads - > map [ threads - > nr + i ] = atoi ( namelist [ i ] - > d_name ) ;
for ( i = 0 ; i < items ; i + + )
free ( namelist [ i ] ) ;
free ( namelist ) ;
threads - > nr + = items ;
}
out_closedir :
closedir ( proc ) ;
out :
return threads ;
out_free_threads :
free ( threads ) ;
return NULL ;
out_free_namelist :
for ( i = 0 ; i < items ; i + + )
free ( namelist [ i ] ) ;
free ( namelist ) ;
out_free_closedir :
free ( threads ) ;
threads = NULL ;
goto out_closedir ;
}
struct thread_map * thread_map__new ( pid_t pid , pid_t tid , uid_t uid )
2011-01-18 20:15:24 +03:00
{
if ( pid ! = - 1 )
return thread_map__new_by_pid ( pid ) ;
2012-01-19 20:08:15 +04:00
if ( tid = = - 1 & & uid ! = UINT_MAX )
return thread_map__new_by_uid ( uid ) ;
2011-01-18 20:15:24 +03:00
return thread_map__new_by_tid ( tid ) ;
}
2012-02-08 20:32:52 +04:00
static struct thread_map * thread_map__new_by_pid_str ( const char * pid_str )
{
struct thread_map * threads = NULL , * nt ;
char name [ 256 ] ;
int items , total_tasks = 0 ;
struct dirent * * namelist = NULL ;
int i , j = 0 ;
pid_t pid , prev_pid = INT_MAX ;
char * end_ptr ;
struct str_node * pos ;
struct strlist * slist = strlist__new ( false , pid_str ) ;
if ( ! slist )
return NULL ;
strlist__for_each ( pos , slist ) {
pid = strtol ( pos - > s , & end_ptr , 10 ) ;
if ( pid = = INT_MIN | | pid = = INT_MAX | |
( * end_ptr ! = ' \0 ' & & * end_ptr ! = ' , ' ) )
goto out_free_threads ;
if ( pid = = prev_pid )
continue ;
sprintf ( name , " /proc/%d/task " , pid ) ;
items = scandir ( name , & namelist , filter , NULL ) ;
if ( items < = 0 )
goto out_free_threads ;
total_tasks + = items ;
nt = realloc ( threads , ( sizeof ( * threads ) +
sizeof ( pid_t ) * total_tasks ) ) ;
if ( nt = = NULL )
goto out_free_threads ;
threads = nt ;
if ( threads ) {
for ( i = 0 ; i < items ; i + + )
threads - > map [ j + + ] = atoi ( namelist [ i ] - > d_name ) ;
threads - > nr = total_tasks ;
}
for ( i = 0 ; i < items ; i + + )
free ( namelist [ i ] ) ;
free ( namelist ) ;
if ( ! threads )
break ;
}
out :
strlist__delete ( slist ) ;
return threads ;
out_free_threads :
free ( threads ) ;
threads = NULL ;
goto out ;
}
static struct thread_map * thread_map__new_by_tid_str ( const char * tid_str )
{
struct thread_map * threads = NULL , * nt ;
int ntasks = 0 ;
pid_t tid , prev_tid = INT_MAX ;
char * end_ptr ;
struct str_node * pos ;
struct strlist * slist ;
/* perf-stat expects threads to be generated even if tid not given */
if ( ! tid_str ) {
threads = malloc ( sizeof ( * threads ) + sizeof ( pid_t ) ) ;
if ( threads ! = NULL ) {
2012-02-21 18:54:25 +04:00
threads - > map [ 0 ] = - 1 ;
2012-02-08 20:32:52 +04:00
threads - > nr = 1 ;
}
return threads ;
}
slist = strlist__new ( false , tid_str ) ;
if ( ! slist )
return NULL ;
strlist__for_each ( pos , slist ) {
tid = strtol ( pos - > s , & end_ptr , 10 ) ;
if ( tid = = INT_MIN | | tid = = INT_MAX | |
( * end_ptr ! = ' \0 ' & & * end_ptr ! = ' , ' ) )
goto out_free_threads ;
if ( tid = = prev_tid )
continue ;
ntasks + + ;
nt = realloc ( threads , sizeof ( * threads ) + sizeof ( pid_t ) * ntasks ) ;
if ( nt = = NULL )
goto out_free_threads ;
threads = nt ;
threads - > map [ ntasks - 1 ] = tid ;
threads - > nr = ntasks ;
}
out :
return threads ;
out_free_threads :
free ( threads ) ;
threads = NULL ;
goto out ;
}
struct thread_map * thread_map__new_str ( const char * pid , const char * tid ,
uid_t uid )
{
if ( pid )
return thread_map__new_by_pid_str ( pid ) ;
if ( ! tid & & uid ! = UINT_MAX )
return thread_map__new_by_uid ( uid ) ;
return thread_map__new_by_tid_str ( tid ) ;
}
2011-01-18 20:15:24 +03:00
void thread_map__delete ( struct thread_map * threads )
{
free ( threads ) ;
}
2012-01-19 20:07:23 +04:00
size_t thread_map__fprintf ( struct thread_map * threads , FILE * fp )
{
int i ;
size_t printed = fprintf ( fp , " %d thread%s: " ,
threads - > nr , threads - > nr > 1 ? " s " : " " ) ;
for ( i = 0 ; i < threads - > nr ; + + i )
printed + = fprintf ( fp , " %s%d " , i ? " , " : " " , threads - > map [ i ] ) ;
return printed + fprintf ( fp , " \n " ) ;
}