Fixed MTP to work with TWRP

This commit is contained in:
awab228 2018-06-19 23:16:04 +02:00
commit f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions

119
tools/Makefile Normal file
View file

@ -0,0 +1,119 @@
include scripts/Makefile.include
help:
@echo 'Possible targets:'
@echo ''
@echo ' acpi - ACPI tools'
@echo ' cgroup - cgroup tools'
@echo ' cpupower - a tool for all things x86 CPU power'
@echo ' firewire - the userspace part of nosy, an IEEE-1394 traffic sniffer'
@echo ' hv - tools used when in Hyper-V clients'
@echo ' lguest - a minimal 32-bit x86 hypervisor'
@echo ' perf - Linux performance measurement and analysis tool'
@echo ' selftests - various kernel selftests'
@echo ' turbostat - Intel CPU idle stats and freq reporting tool'
@echo ' usb - USB testing tools'
@echo ' virtio - vhost test module'
@echo ' net - misc networking tools'
@echo ' vm - misc vm tools'
@echo ' x86_energy_perf_policy - Intel energy policy tool'
@echo ' tmon - thermal monitoring and tuning tool'
@echo ''
@echo 'You can do:'
@echo ' $$ make -C tools/ <tool>_install'
@echo ''
@echo ' from the kernel command line to build and install one of'
@echo ' the tools above'
@echo ''
@echo ' $$ make tools/install'
@echo ''
@echo ' installs all tools.'
@echo ''
@echo 'Cleaning targets:'
@echo ''
@echo ' all of the above with the "_clean" string appended cleans'
@echo ' the respective build directory.'
@echo ' clean: a summary clean target to clean _all_ folders'
acpi: FORCE
$(call descend,power/$@)
cpupower: FORCE
$(call descend,power/$@)
cgroup firewire hv guest usb virtio vm net: FORCE
$(call descend,$@)
liblockdep: FORCE
$(call descend,lib/lockdep)
libapikfs: FORCE
$(call descend,lib/api)
perf: libapikfs FORCE
$(call descend,$@)
selftests: FORCE
$(call descend,testing/$@)
turbostat x86_energy_perf_policy: FORCE
$(call descend,power/x86/$@)
tmon: FORCE
$(call descend,thermal/$@)
acpi_install:
$(call descend,power/$(@:_install=),install)
cpupower_install:
$(call descend,power/$(@:_install=),install)
cgroup_install firewire_install hv_install lguest_install perf_install usb_install virtio_install vm_install net_install:
$(call descend,$(@:_install=),install)
selftests_install:
$(call descend,testing/$(@:_clean=),install)
turbostat_install x86_energy_perf_policy_install:
$(call descend,power/x86/$(@:_install=),install)
tmon_install:
$(call descend,thermal/$(@:_install=),install)
install: acpi_install cgroup_install cpupower_install hv_install firewire_install lguest_install \
perf_install selftests_install turbostat_install usb_install \
virtio_install vm_install net_install x86_energy_perf_policy_install \
tmon
acpi_clean:
$(call descend,power/acpi,clean)
cpupower_clean:
$(call descend,power/cpupower,clean)
cgroup_clean hv_clean firewire_clean lguest_clean usb_clean virtio_clean vm_clean net_clean:
$(call descend,$(@:_clean=),clean)
liblockdep_clean:
$(call descend,lib/lockdep,clean)
libapikfs_clean:
$(call descend,lib/api,clean)
perf_clean: libapikfs_clean
$(call descend,$(@:_clean=),clean)
selftests_clean:
$(call descend,testing/$(@:_clean=),clean)
turbostat_clean x86_energy_perf_policy_clean:
$(call descend,power/x86/$(@:_clean=),clean)
tmon_clean:
$(call descend,thermal/tmon,clean)
clean: acpi_clean cgroup_clean cpupower_clean hv_clean firewire_clean lguest_clean \
perf_clean selftests_clean turbostat_clean usb_clean virtio_clean \
vm_clean net_clean x86_energy_perf_policy_clean tmon_clean
.PHONY: FORCE

11
tools/cgroup/Makefile Normal file
View file

@ -0,0 +1,11 @@
# Makefile for cgroup tools
CC = $(CROSS_COMPILE)gcc
CFLAGS = -Wall -Wextra
all: cgroup_event_listener
%: %.c
$(CC) $(CFLAGS) -o $@ $^
clean:
$(RM) cgroup_event_listener

View file

@ -0,0 +1,82 @@
/*
* cgroup_event_listener.c - Simple listener of cgroup events
*
* Copyright (C) Kirill A. Shutemov <kirill@shutemov.name>
*/
#include <assert.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <libgen.h>
#include <limits.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/eventfd.h>
#define USAGE_STR "Usage: cgroup_event_listener <path-to-control-file> <args>"
int main(int argc, char **argv)
{
int efd = -1;
int cfd = -1;
int event_control = -1;
char event_control_path[PATH_MAX];
char line[LINE_MAX];
int ret;
if (argc != 3)
errx(1, "%s", USAGE_STR);
cfd = open(argv[1], O_RDONLY);
if (cfd == -1)
err(1, "Cannot open %s", argv[1]);
ret = snprintf(event_control_path, PATH_MAX, "%s/cgroup.event_control",
dirname(argv[1]));
if (ret >= PATH_MAX)
errx(1, "Path to cgroup.event_control is too long");
event_control = open(event_control_path, O_WRONLY);
if (event_control == -1)
err(1, "Cannot open %s", event_control_path);
efd = eventfd(0, 0);
if (efd == -1)
err(1, "eventfd() failed");
ret = snprintf(line, LINE_MAX, "%d %d %s", efd, cfd, argv[2]);
if (ret >= LINE_MAX)
errx(1, "Arguments string is too long");
ret = write(event_control, line, strlen(line) + 1);
if (ret == -1)
err(1, "Cannot write to cgroup.event_control");
while (1) {
uint64_t result;
ret = read(efd, &result, sizeof(result));
if (ret == -1) {
if (errno == EINTR)
continue;
err(1, "Cannot read from eventfd");
}
assert(ret == sizeof(result));
ret = access(event_control_path, W_OK);
if ((ret == -1) && (errno == ENOENT)) {
puts("The cgroup seems to have removed.");
break;
}
if (ret == -1)
err(1, "cgroup.event_control is not accessible any more");
printf("%s %s: crossed\n", argv[1], argv[2]);
}
return 0;
}

BIN
tools/dtbtool Executable file

Binary file not shown.

19
tools/firewire/Makefile Normal file
View file

@ -0,0 +1,19 @@
prefix = /usr
nosy-dump-version = 0.4
CC = gcc
all : nosy-dump
nosy-dump : CFLAGS = -Wall -O2 -g
nosy-dump : CPPFLAGS = -DVERSION=\"$(nosy-dump-version)\" -I../../drivers/firewire
nosy-dump : LDFLAGS = -g
nosy-dump : LDLIBS = -lpopt
nosy-dump : nosy-dump.o decode-fcp.o
clean :
rm -rf *.o nosy-dump
install :
install nosy-dump $(prefix)/bin/nosy-dump

213
tools/firewire/decode-fcp.c Normal file
View file

@ -0,0 +1,213 @@
#include <linux/firewire-constants.h>
#include <stdio.h>
#include <stdlib.h>
#include "list.h"
#include "nosy-dump.h"
#define CSR_FCP_COMMAND 0xfffff0000b00ull
#define CSR_FCP_RESPONSE 0xfffff0000d00ull
static const char * const ctype_names[] = {
[0x0] = "control", [0x8] = "not implemented",
[0x1] = "status", [0x9] = "accepted",
[0x2] = "specific inquiry", [0xa] = "rejected",
[0x3] = "notify", [0xb] = "in transition",
[0x4] = "general inquiry", [0xc] = "stable",
[0x5] = "(reserved 0x05)", [0xd] = "changed",
[0x6] = "(reserved 0x06)", [0xe] = "(reserved 0x0e)",
[0x7] = "(reserved 0x07)", [0xf] = "interim",
};
static const char * const subunit_type_names[] = {
[0x00] = "monitor", [0x10] = "(reserved 0x10)",
[0x01] = "audio", [0x11] = "(reserved 0x11)",
[0x02] = "printer", [0x12] = "(reserved 0x12)",
[0x03] = "disc", [0x13] = "(reserved 0x13)",
[0x04] = "tape recorder/player",[0x14] = "(reserved 0x14)",
[0x05] = "tuner", [0x15] = "(reserved 0x15)",
[0x06] = "ca", [0x16] = "(reserved 0x16)",
[0x07] = "camera", [0x17] = "(reserved 0x17)",
[0x08] = "(reserved 0x08)", [0x18] = "(reserved 0x18)",
[0x09] = "panel", [0x19] = "(reserved 0x19)",
[0x0a] = "bulletin board", [0x1a] = "(reserved 0x1a)",
[0x0b] = "camera storage", [0x1b] = "(reserved 0x1b)",
[0x0c] = "(reserved 0x0c)", [0x1c] = "vendor unique",
[0x0d] = "(reserved 0x0d)", [0x1d] = "all subunit types",
[0x0e] = "(reserved 0x0e)", [0x1e] = "subunit_type extended to next byte",
[0x0f] = "(reserved 0x0f)", [0x1f] = "unit",
};
struct avc_enum {
int value;
const char *name;
};
struct avc_field {
const char *name; /* Short name for field. */
int offset; /* Location of field, specified in bits; */
/* negative means from end of packet. */
int width; /* Width of field, 0 means use data_length. */
struct avc_enum *names;
};
struct avc_opcode_info {
const char *name;
struct avc_field fields[8];
};
struct avc_enum power_field_names[] = {
{ 0x70, "on" },
{ 0x60, "off" },
{ }
};
static const struct avc_opcode_info opcode_info[256] = {
/* TA Document 1999026 */
/* AV/C Digital Interface Command Set General Specification 4.0 */
[0xb2] = { "power", {
{ "state", 0, 8, power_field_names }
}
},
[0x30] = { "unit info", {
{ "foo", 0, 8 },
{ "unit_type", 8, 5 },
{ "unit", 13, 3 },
{ "company id", 16, 24 },
}
},
[0x31] = { "subunit info" },
[0x01] = { "reserve" },
[0xb0] = { "version" },
[0x00] = { "vendor dependent" },
[0x02] = { "plug info" },
[0x12] = { "channel usage" },
[0x24] = { "connect" },
[0x20] = { "connect av" },
[0x22] = { "connections" },
[0x11] = { "digital input" },
[0x10] = { "digital output" },
[0x25] = { "disconnect" },
[0x21] = { "disconnect av" },
[0x19] = { "input plug signal format" },
[0x18] = { "output plug signal format" },
[0x1f] = { "general bus setup" },
/* TA Document 1999025 */
/* AV/C Descriptor Mechanism Specification Version 1.0 */
[0x0c] = { "create descriptor" },
[0x08] = { "open descriptor" },
[0x09] = { "read descriptor" },
[0x0a] = { "write descriptor" },
[0x05] = { "open info block" },
[0x06] = { "read info block" },
[0x07] = { "write info block" },
[0x0b] = { "search descriptor" },
[0x0d] = { "object number select" },
/* TA Document 1999015 */
/* AV/C Command Set for Rate Control of Isochronous Data Flow 1.0 */
[0xb3] = { "rate", {
{ "subfunction", 0, 8 },
{ "result", 8, 8 },
{ "plug_type", 16, 8 },
{ "plug_id", 16, 8 },
}
},
/* TA Document 1999008 */
/* AV/C Audio Subunit Specification 1.0 */
[0xb8] = { "function block" },
/* TA Document 2001001 */
/* AV/C Panel Subunit Specification 1.1 */
[0x7d] = { "gui update" },
[0x7e] = { "push gui data" },
[0x7f] = { "user action" },
[0x7c] = { "pass through" },
/* */
[0x26] = { "asynchronous connection" },
};
struct avc_frame {
uint32_t operand0:8;
uint32_t opcode:8;
uint32_t subunit_id:3;
uint32_t subunit_type:5;
uint32_t ctype:4;
uint32_t cts:4;
};
static void
decode_avc(struct link_transaction *t)
{
struct avc_frame *frame =
(struct avc_frame *) t->request->packet.write_block.data;
const struct avc_opcode_info *info;
const char *name;
char buffer[32];
int i;
info = &opcode_info[frame->opcode];
if (info->name == NULL) {
snprintf(buffer, sizeof(buffer),
"(unknown opcode 0x%02x)", frame->opcode);
name = buffer;
} else {
name = info->name;
}
printf("av/c %s, subunit_type=%s, subunit_id=%d, opcode=%s",
ctype_names[frame->ctype], subunit_type_names[frame->subunit_type],
frame->subunit_id, name);
for (i = 0; info->fields[i].name != NULL; i++)
printf(", %s", info->fields[i].name);
printf("\n");
}
int
decode_fcp(struct link_transaction *t)
{
struct avc_frame *frame =
(struct avc_frame *) t->request->packet.write_block.data;
unsigned long long offset =
((unsigned long long) t->request->packet.common.offset_high << 32) |
t->request->packet.common.offset_low;
if (t->request->packet.common.tcode != TCODE_WRITE_BLOCK_REQUEST)
return 0;
if (offset == CSR_FCP_COMMAND || offset == CSR_FCP_RESPONSE) {
switch (frame->cts) {
case 0x00:
decode_avc(t);
break;
case 0x01:
printf("cal fcp frame (cts=0x01)\n");
break;
case 0x02:
printf("ehs fcp frame (cts=0x02)\n");
break;
case 0x03:
printf("havi fcp frame (cts=0x03)\n");
break;
case 0x0e:
printf("vendor specific fcp frame (cts=0x0e)\n");
break;
case 0x0f:
printf("extended cts\n");
break;
default:
printf("reserved fcp frame (ctx=0x%02x)\n", frame->cts);
break;
}
return 1;
}
return 0;
}

62
tools/firewire/list.h Normal file
View file

@ -0,0 +1,62 @@
struct list {
struct list *next, *prev;
};
static inline void
list_init(struct list *list)
{
list->next = list;
list->prev = list;
}
static inline int
list_empty(struct list *list)
{
return list->next == list;
}
static inline void
list_insert(struct list *link, struct list *new_link)
{
new_link->prev = link->prev;
new_link->next = link;
new_link->prev->next = new_link;
new_link->next->prev = new_link;
}
static inline void
list_append(struct list *list, struct list *new_link)
{
list_insert((struct list *)list, new_link);
}
static inline void
list_prepend(struct list *list, struct list *new_link)
{
list_insert(list->next, new_link);
}
static inline void
list_remove(struct list *link)
{
link->prev->next = link->next;
link->next->prev = link->prev;
}
#define list_entry(link, type, member) \
((type *)((char *)(link)-(unsigned long)(&((type *)0)->member)))
#define list_head(list, type, member) \
list_entry((list)->next, type, member)
#define list_tail(list, type, member) \
list_entry((list)->prev, type, member)
#define list_next(elm, member) \
list_entry((elm)->member.next, typeof(*elm), member)
#define list_for_each_entry(pos, list, member) \
for (pos = list_head(list, typeof(*pos), member); \
&pos->member != (list); \
pos = list_next(pos, member))

1035
tools/firewire/nosy-dump.c Normal file

File diff suppressed because it is too large Load diff

173
tools/firewire/nosy-dump.h Normal file
View file

@ -0,0 +1,173 @@
#ifndef __nosy_dump_h__
#define __nosy_dump_h__
#define array_length(array) (sizeof(array) / sizeof(array[0]))
#define ACK_NO_ACK 0x0
#define ACK_DONE(a) ((a >> 2) == 0)
#define ACK_BUSY(a) ((a >> 2) == 1)
#define ACK_ERROR(a) ((a >> 2) == 3)
#include <stdint.h>
struct phy_packet {
uint32_t timestamp;
union {
struct {
uint32_t zero:24;
uint32_t phy_id:6;
uint32_t identifier:2;
} common, link_on;
struct {
uint32_t zero:16;
uint32_t gap_count:6;
uint32_t set_gap_count:1;
uint32_t set_root:1;
uint32_t root_id:6;
uint32_t identifier:2;
} phy_config;
struct {
uint32_t more_packets:1;
uint32_t initiated_reset:1;
uint32_t port2:2;
uint32_t port1:2;
uint32_t port0:2;
uint32_t power_class:3;
uint32_t contender:1;
uint32_t phy_delay:2;
uint32_t phy_speed:2;
uint32_t gap_count:6;
uint32_t link_active:1;
uint32_t extended:1;
uint32_t phy_id:6;
uint32_t identifier:2;
} self_id;
struct {
uint32_t more_packets:1;
uint32_t reserved1:1;
uint32_t porth:2;
uint32_t portg:2;
uint32_t portf:2;
uint32_t porte:2;
uint32_t portd:2;
uint32_t portc:2;
uint32_t portb:2;
uint32_t porta:2;
uint32_t reserved0:2;
uint32_t sequence:3;
uint32_t extended:1;
uint32_t phy_id:6;
uint32_t identifier:2;
} ext_self_id;
};
uint32_t inverted;
uint32_t ack;
};
#define TCODE_PHY_PACKET 0x10
#define PHY_PACKET_CONFIGURATION 0x00
#define PHY_PACKET_LINK_ON 0x01
#define PHY_PACKET_SELF_ID 0x02
struct link_packet {
uint32_t timestamp;
union {
struct {
uint32_t priority:4;
uint32_t tcode:4;
uint32_t rt:2;
uint32_t tlabel:6;
uint32_t destination:16;
uint32_t offset_high:16;
uint32_t source:16;
uint32_t offset_low;
} common;
struct {
uint32_t common[3];
uint32_t crc;
} read_quadlet;
struct {
uint32_t common[3];
uint32_t data;
uint32_t crc;
} read_quadlet_response;
struct {
uint32_t common[3];
uint32_t extended_tcode:16;
uint32_t data_length:16;
uint32_t crc;
} read_block;
struct {
uint32_t common[3];
uint32_t extended_tcode:16;
uint32_t data_length:16;
uint32_t crc;
uint32_t data[0];
/* crc and ack follows. */
} read_block_response;
struct {
uint32_t common[3];
uint32_t data;
uint32_t crc;
} write_quadlet;
struct {
uint32_t common[3];
uint32_t extended_tcode:16;
uint32_t data_length:16;
uint32_t crc;
uint32_t data[0];
/* crc and ack follows. */
} write_block;
struct {
uint32_t common[3];
uint32_t crc;
} write_response;
struct {
uint32_t common[3];
uint32_t data;
uint32_t crc;
} cycle_start;
struct {
uint32_t sy:4;
uint32_t tcode:4;
uint32_t channel:6;
uint32_t tag:2;
uint32_t data_length:16;
uint32_t crc;
} iso_data;
};
};
struct subaction {
uint32_t ack;
size_t length;
struct list link;
struct link_packet packet;
};
struct link_transaction {
int request_node, response_node, tlabel;
struct subaction *request, *response;
struct list request_list, response_list;
struct list link;
};
int decode_fcp(struct link_transaction *t);
#endif /* __nosy_dump_h__ */

13
tools/hv/Makefile Normal file
View file

@ -0,0 +1,13 @@
# Makefile for Hyper-V tools
CC = $(CROSS_COMPILE)gcc
PTHREAD_LIBS = -lpthread
WARNINGS = -Wall -Wextra
CFLAGS = $(WARNINGS) -g $(PTHREAD_LIBS)
all: hv_kvp_daemon hv_vss_daemon
%: %.c
$(CC) $(CFLAGS) -o $@ $^
clean:
$(RM) hv_kvp_daemon hv_vss_daemon

198
tools/hv/hv_fcopy_daemon.c Normal file
View file

@ -0,0 +1,198 @@
/*
* An implementation of host to guest copy functionality for Linux.
*
* Copyright (C) 2014, Microsoft, Inc.
*
* Author : K. Y. Srinivasan <kys@microsoft.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published
* by the Free Software Foundation.
*
* This program 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, GOOD TITLE or
* NON INFRINGEMENT. See the GNU General Public License for more
* details.
*/
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/poll.h>
#include <linux/types.h>
#include <linux/kdev_t.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <linux/hyperv.h>
#include <syslog.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <dirent.h>
static int target_fd;
static char target_fname[W_MAX_PATH];
static int hv_start_fcopy(struct hv_start_fcopy *smsg)
{
int error = HV_E_FAIL;
char *q, *p;
/*
* If possile append a path seperator to the path.
*/
if (strlen((char *)smsg->path_name) < (W_MAX_PATH - 2))
strcat((char *)smsg->path_name, "/");
p = (char *)smsg->path_name;
snprintf(target_fname, sizeof(target_fname), "%s/%s",
(char *)smsg->path_name, smsg->file_name);
syslog(LOG_INFO, "Target file name: %s", target_fname);
/*
* Check to see if the path is already in place; if not,
* create if required.
*/
while ((q = strchr(p, '/')) != NULL) {
if (q == p) {
p++;
continue;
}
*q = '\0';
if (access((char *)smsg->path_name, F_OK)) {
if (smsg->copy_flags & CREATE_PATH) {
if (mkdir((char *)smsg->path_name, 0755)) {
syslog(LOG_ERR, "Failed to create %s",
(char *)smsg->path_name);
goto done;
}
} else {
syslog(LOG_ERR, "Invalid path: %s",
(char *)smsg->path_name);
goto done;
}
}
p = q + 1;
*q = '/';
}
if (!access(target_fname, F_OK)) {
syslog(LOG_INFO, "File: %s exists", target_fname);
if (!(smsg->copy_flags & OVER_WRITE)) {
error = HV_ERROR_ALREADY_EXISTS;
goto done;
}
}
target_fd = open(target_fname,
O_RDWR | O_CREAT | O_TRUNC | O_CLOEXEC, 0744);
if (target_fd == -1) {
syslog(LOG_INFO, "Open Failed: %s", strerror(errno));
goto done;
}
error = 0;
done:
return error;
}
static int hv_copy_data(struct hv_do_fcopy *cpmsg)
{
ssize_t bytes_written;
bytes_written = pwrite(target_fd, cpmsg->data, cpmsg->size,
cpmsg->offset);
if (bytes_written != cpmsg->size)
return HV_E_FAIL;
return 0;
}
static int hv_copy_finished(void)
{
close(target_fd);
return 0;
}
static int hv_copy_cancel(void)
{
close(target_fd);
unlink(target_fname);
return 0;
}
int main(void)
{
int fd, fcopy_fd, len;
int error;
int version = FCOPY_CURRENT_VERSION;
char *buffer[4096 * 2];
struct hv_fcopy_hdr *in_msg;
if (daemon(1, 0)) {
syslog(LOG_ERR, "daemon() failed; error: %s", strerror(errno));
exit(EXIT_FAILURE);
}
openlog("HV_FCOPY", 0, LOG_USER);
syslog(LOG_INFO, "HV_FCOPY starting; pid is:%d", getpid());
fcopy_fd = open("/dev/vmbus/hv_fcopy", O_RDWR);
if (fcopy_fd < 0) {
syslog(LOG_ERR, "open /dev/vmbus/hv_fcopy failed; error: %d %s",
errno, strerror(errno));
exit(EXIT_FAILURE);
}
/*
* Register with the kernel.
*/
if ((write(fcopy_fd, &version, sizeof(int))) != sizeof(int)) {
syslog(LOG_ERR, "Registration failed: %s", strerror(errno));
exit(EXIT_FAILURE);
}
while (1) {
/*
* In this loop we process fcopy messages after the
* handshake is complete.
*/
len = pread(fcopy_fd, buffer, (4096 * 2), 0);
if (len < 0) {
syslog(LOG_ERR, "pread failed: %s", strerror(errno));
exit(EXIT_FAILURE);
}
in_msg = (struct hv_fcopy_hdr *)buffer;
switch (in_msg->operation) {
case START_FILE_COPY:
error = hv_start_fcopy((struct hv_start_fcopy *)in_msg);
break;
case WRITE_TO_FILE:
error = hv_copy_data((struct hv_do_fcopy *)in_msg);
break;
case COMPLETE_FCOPY:
error = hv_copy_finished();
break;
case CANCEL_FCOPY:
error = hv_copy_cancel();
break;
default:
syslog(LOG_ERR, "Unknown operation: %d",
in_msg->operation);
}
if (pwrite(fcopy_fd, &error, sizeof(int), 0) != sizeof(int)) {
syslog(LOG_ERR, "pwrite failed: %s", strerror(errno));
exit(EXIT_FAILURE);
}
}
}

28
tools/hv/hv_get_dhcp_info.sh Executable file
View file

@ -0,0 +1,28 @@
#!/bin/bash
# This example script retrieves the DHCP state of a given interface.
# In the interest of keeping the KVP daemon code free of distro specific
# information; the kvp daemon code invokes this external script to gather
# DHCP setting for the specific interface.
#
# Input: Name of the interface
#
# Output: The script prints the string "Enabled" to stdout to indicate
# that DHCP is enabled on the interface. If DHCP is not enabled,
# the script prints the string "Disabled" to stdout.
#
# Each Distro is expected to implement this script in a distro specific
# fashion. For instance on Distros that ship with Network Manager enabled,
# this script can be based on the Network Manager APIs for retrieving DHCP
# information.
if_file="/etc/sysconfig/network-scripts/ifcfg-"$1
dhcp=$(grep "dhcp" $if_file 2>/dev/null)
if [ "$dhcp" != "" ];
then
echo "Enabled"
else
echo "Disabled"
fi

13
tools/hv/hv_get_dns_info.sh Executable file
View file

@ -0,0 +1,13 @@
#!/bin/bash
# This example script parses /etc/resolv.conf to retrive DNS information.
# In the interest of keeping the KVP daemon code free of distro specific
# information; the kvp daemon code invokes this external script to gather
# DNS information.
# This script is expected to print the nameserver values to stdout.
# Each Distro is expected to implement this script in a distro specific
# fashion. For instance on Distros that ship with Network Manager enabled,
# this script can be based on the Network Manager APIs for retrieving DNS
# entries.
cat /etc/resolv.conf 2>/dev/null | awk '/^nameserver/ { print $2 }'

1742
tools/hv/hv_kvp_daemon.c Normal file

File diff suppressed because it is too large Load diff

64
tools/hv/hv_set_ifconfig.sh Executable file
View file

@ -0,0 +1,64 @@
#!/bin/bash
# This example script activates an interface based on the specified
# configuration.
#
# In the interest of keeping the KVP daemon code free of distro specific
# information; the kvp daemon code invokes this external script to configure
# the interface.
#
# The only argument to this script is the configuration file that is to
# be used to configure the interface.
#
# Each Distro is expected to implement this script in a distro specific
# fashion. For instance on Distros that ship with Network Manager enabled,
# this script can be based on the Network Manager APIs for configuring the
# interface.
#
# This example script is based on a RHEL environment.
#
# Here is the format of the ip configuration file:
#
# HWADDR=macaddr
# DEVICE=interface name
# BOOTPROTO=<protocol> (where <protocol> is "dhcp" if DHCP is configured
# or "none" if no boot-time protocol should be used)
#
# IPADDR0=ipaddr1
# IPADDR1=ipaddr2
# IPADDRx=ipaddry (where y = x + 1)
#
# NETMASK0=netmask1
# NETMASKx=netmasky (where y = x + 1)
#
# GATEWAY=ipaddr1
# GATEWAYx=ipaddry (where y = x + 1)
#
# DNSx=ipaddrx (where first DNS address is tagged as DNS1 etc)
#
# IPV6 addresses will be tagged as IPV6ADDR, IPV6 gateway will be
# tagged as IPV6_DEFAULTGW and IPV6 NETMASK will be tagged as
# IPV6NETMASK.
#
# The host can specify multiple ipv4 and ipv6 addresses to be
# configured for the interface. Furthermore, the configuration
# needs to be persistent. A subsequent GET call on the interface
# is expected to return the configuration that is set via the SET
# call.
#
echo "IPV6INIT=yes" >> $1
echo "NM_CONTROLLED=no" >> $1
echo "PEERDNS=yes" >> $1
echo "ONBOOT=yes" >> $1
cp $1 /etc/sysconfig/network-scripts/
interface=$(echo $1 | awk -F - '{ print $2 }')
/sbin/ifdown $interface 2>/dev/null
/sbin/ifup $interface 2>/dev/null

267
tools/hv/hv_vss_daemon.c Normal file
View file

@ -0,0 +1,267 @@
/*
* An implementation of the host initiated guest snapshot for Hyper-V.
*
*
* Copyright (C) 2013, Microsoft, Inc.
* Author : K. Y. Srinivasan <kys@microsoft.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published
* by the Free Software Foundation.
*
* This program 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, GOOD TITLE or
* NON INFRINGEMENT. See the GNU General Public License for more
* details.
*
*/
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/poll.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <stdio.h>
#include <mntent.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <arpa/inet.h>
#include <linux/fs.h>
#include <linux/connector.h>
#include <linux/hyperv.h>
#include <linux/netlink.h>
#include <syslog.h>
static struct sockaddr_nl addr;
#ifndef SOL_NETLINK
#define SOL_NETLINK 270
#endif
static int vss_do_freeze(char *dir, unsigned int cmd, char *fs_op)
{
int ret, fd = open(dir, O_RDONLY);
if (fd < 0)
return 1;
ret = ioctl(fd, cmd, 0);
syslog(LOG_INFO, "VSS: %s of %s: %s\n", fs_op, dir, strerror(errno));
close(fd);
return !!ret;
}
static int vss_operate(int operation)
{
char *fs_op;
char match[] = "/dev/";
FILE *mounts;
struct mntent *ent;
unsigned int cmd;
int error = 0, root_seen = 0;
switch (operation) {
case VSS_OP_FREEZE:
cmd = FIFREEZE;
fs_op = "freeze";
break;
case VSS_OP_THAW:
cmd = FITHAW;
fs_op = "thaw";
break;
default:
return -1;
}
mounts = setmntent("/proc/mounts", "r");
if (mounts == NULL)
return -1;
while ((ent = getmntent(mounts))) {
if (strncmp(ent->mnt_fsname, match, strlen(match)))
continue;
if (strcmp(ent->mnt_type, "iso9660") == 0)
continue;
if (strcmp(ent->mnt_type, "vfat") == 0)
continue;
if (strcmp(ent->mnt_dir, "/") == 0) {
root_seen = 1;
continue;
}
error |= vss_do_freeze(ent->mnt_dir, cmd, fs_op);
}
endmntent(mounts);
if (root_seen) {
error |= vss_do_freeze("/", cmd, fs_op);
}
return error;
}
static int netlink_send(int fd, struct cn_msg *msg)
{
struct nlmsghdr nlh = { .nlmsg_type = NLMSG_DONE };
unsigned int size;
struct msghdr message;
struct iovec iov[2];
size = sizeof(struct cn_msg) + msg->len;
nlh.nlmsg_pid = getpid();
nlh.nlmsg_len = NLMSG_LENGTH(size);
iov[0].iov_base = &nlh;
iov[0].iov_len = sizeof(nlh);
iov[1].iov_base = msg;
iov[1].iov_len = size;
memset(&message, 0, sizeof(message));
message.msg_name = &addr;
message.msg_namelen = sizeof(addr);
message.msg_iov = iov;
message.msg_iovlen = 2;
return sendmsg(fd, &message, 0);
}
int main(void)
{
int fd, len, nl_group;
int error;
struct cn_msg *message;
struct pollfd pfd;
struct nlmsghdr *incoming_msg;
struct cn_msg *incoming_cn_msg;
int op;
struct hv_vss_msg *vss_msg;
char *vss_recv_buffer;
size_t vss_recv_buffer_len;
if (daemon(1, 0))
return 1;
openlog("Hyper-V VSS", 0, LOG_USER);
syslog(LOG_INFO, "VSS starting; pid is:%d", getpid());
vss_recv_buffer_len = NLMSG_LENGTH(0) + sizeof(struct cn_msg) + sizeof(struct hv_vss_msg);
vss_recv_buffer = calloc(1, vss_recv_buffer_len);
if (!vss_recv_buffer) {
syslog(LOG_ERR, "Failed to allocate netlink buffers");
exit(EXIT_FAILURE);
}
fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_CONNECTOR);
if (fd < 0) {
syslog(LOG_ERR, "netlink socket creation failed; error:%d %s",
errno, strerror(errno));
exit(EXIT_FAILURE);
}
addr.nl_family = AF_NETLINK;
addr.nl_pad = 0;
addr.nl_pid = 0;
addr.nl_groups = 0;
error = bind(fd, (struct sockaddr *)&addr, sizeof(addr));
if (error < 0) {
syslog(LOG_ERR, "bind failed; error:%d %s", errno, strerror(errno));
close(fd);
exit(EXIT_FAILURE);
}
nl_group = CN_VSS_IDX;
if (setsockopt(fd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP, &nl_group, sizeof(nl_group)) < 0) {
syslog(LOG_ERR, "setsockopt failed; error:%d %s", errno, strerror(errno));
close(fd);
exit(EXIT_FAILURE);
}
/*
* Register ourselves with the kernel.
*/
message = (struct cn_msg *)vss_recv_buffer;
message->id.idx = CN_VSS_IDX;
message->id.val = CN_VSS_VAL;
message->ack = 0;
vss_msg = (struct hv_vss_msg *)message->data;
vss_msg->vss_hdr.operation = VSS_OP_REGISTER;
message->len = sizeof(struct hv_vss_msg);
len = netlink_send(fd, message);
if (len < 0) {
syslog(LOG_ERR, "netlink_send failed; error:%d %s", errno, strerror(errno));
close(fd);
exit(EXIT_FAILURE);
}
pfd.fd = fd;
while (1) {
struct sockaddr *addr_p = (struct sockaddr *) &addr;
socklen_t addr_l = sizeof(addr);
pfd.events = POLLIN;
pfd.revents = 0;
if (poll(&pfd, 1, -1) < 0) {
syslog(LOG_ERR, "poll failed; error:%d %s", errno, strerror(errno));
if (errno == EINVAL) {
close(fd);
exit(EXIT_FAILURE);
}
else
continue;
}
len = recvfrom(fd, vss_recv_buffer, vss_recv_buffer_len, 0,
addr_p, &addr_l);
if (len < 0) {
syslog(LOG_ERR, "recvfrom failed; pid:%u error:%d %s",
addr.nl_pid, errno, strerror(errno));
close(fd);
return -1;
}
if (addr.nl_pid) {
syslog(LOG_WARNING,
"Received packet from untrusted pid:%u",
addr.nl_pid);
continue;
}
incoming_msg = (struct nlmsghdr *)vss_recv_buffer;
if (incoming_msg->nlmsg_type != NLMSG_DONE)
continue;
incoming_cn_msg = (struct cn_msg *)NLMSG_DATA(incoming_msg);
vss_msg = (struct hv_vss_msg *)incoming_cn_msg->data;
op = vss_msg->vss_hdr.operation;
error = HV_S_OK;
switch (op) {
case VSS_OP_FREEZE:
case VSS_OP_THAW:
error = vss_operate(op);
if (error)
error = HV_E_FAIL;
break;
default:
syslog(LOG_ERR, "Illegal op:%d\n", op);
}
vss_msg->error = error;
len = netlink_send(fd, incoming_cn_msg);
if (len < 0) {
syslog(LOG_ERR, "net_link send failed; error:%d %s",
errno, strerror(errno));
exit(EXIT_FAILURE);
}
}
}

25
tools/include/asm/bug.h Normal file
View file

@ -0,0 +1,25 @@
#ifndef _TOOLS_ASM_BUG_H
#define _TOOLS_ASM_BUG_H
#include <linux/compiler.h>
#define __WARN_printf(arg...) do { fprintf(stderr, arg); } while (0)
#define WARN(condition, format...) ({ \
int __ret_warn_on = !!(condition); \
if (unlikely(__ret_warn_on)) \
__WARN_printf(format); \
unlikely(__ret_warn_on); \
})
#define WARN_ONCE(condition, format...) ({ \
static int __warned; \
int __ret_warn_once = !!(condition); \
\
if (unlikely(__ret_warn_once)) \
if (WARN(!__warned, format)) \
__warned = 1; \
unlikely(__ret_warn_once); \
})
#endif /* _TOOLS_ASM_BUG_H */

View file

@ -0,0 +1,40 @@
#ifndef _TOOLS_LINUX_COMPILER_H_
#define _TOOLS_LINUX_COMPILER_H_
#ifndef __always_inline
# define __always_inline inline __attribute__((always_inline))
#endif
#define __user
#ifndef __attribute_const__
# define __attribute_const__
#endif
#ifndef __maybe_unused
# define __maybe_unused __attribute__((unused))
#endif
#ifndef __packed
# define __packed __attribute__((__packed__))
#endif
#ifndef __force
# define __force
#endif
#ifndef __weak
# define __weak __attribute__((weak))
#endif
#ifndef likely
# define likely(x) __builtin_expect(!!(x), 1)
#endif
#ifndef unlikely
# define unlikely(x) __builtin_expect(!!(x), 0)
#endif
#define ACCESS_ONCE(x) (*(volatile typeof(x) *)&(x))
#endif /* _TOOLS_LINUX_COMPILER_H */

View file

@ -0,0 +1,10 @@
#ifndef _TOOLS_LINUX_EXPORT_H_
#define _TOOLS_LINUX_EXPORT_H_
#define EXPORT_SYMBOL(sym)
#define EXPORT_SYMBOL_GPL(sym)
#define EXPORT_SYMBOL_GPL_FUTURE(sym)
#define EXPORT_UNUSED_SYMBOL(sym)
#define EXPORT_UNUSED_SYMBOL_GPL(sym)
#endif

View file

@ -0,0 +1,5 @@
#include "../../../include/linux/hash.h"
#ifndef _TOOLS_LINUX_HASH_H
#define _TOOLS_LINUX_HASH_H
#endif

View file

@ -0,0 +1,75 @@
#ifndef _TOOLS_LINUX_TYPES_H_
#define _TOOLS_LINUX_TYPES_H_
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#define __SANE_USERSPACE_TYPES__ /* For PPC64, to get LL64 types */
#include <asm/types.h>
struct page;
struct kmem_cache;
typedef enum {
GFP_KERNEL,
GFP_ATOMIC,
__GFP_HIGHMEM,
__GFP_HIGH
} gfp_t;
/*
* We define u64 as uint64_t for every architecture
* so that we can print it with "%"PRIx64 without getting warnings.
*
* typedef __u64 u64;
* typedef __s64 s64;
*/
typedef uint64_t u64;
typedef int64_t s64;
typedef __u32 u32;
typedef __s32 s32;
typedef __u16 u16;
typedef __s16 s16;
typedef __u8 u8;
typedef __s8 s8;
#ifdef __CHECKER__
#define __bitwise__ __attribute__((bitwise))
#else
#define __bitwise__
#endif
#ifdef __CHECK_ENDIAN__
#define __bitwise __bitwise__
#else
#define __bitwise
#endif
#define __force
#define __user
#define __must_check
#define __cold
typedef __u16 __bitwise __le16;
typedef __u16 __bitwise __be16;
typedef __u32 __bitwise __le32;
typedef __u32 __bitwise __be32;
typedef __u64 __bitwise __le64;
typedef __u64 __bitwise __be64;
struct list_head {
struct list_head *next, *prev;
};
struct hlist_head {
struct hlist_node *first;
};
struct hlist_node {
struct hlist_node *next, **pprev;
};
#endif /* _TOOLS_LINUX_TYPES_H_ */

View file

@ -0,0 +1,70 @@
#ifndef _TOOLS_BE_BYTESHIFT_H
#define _TOOLS_BE_BYTESHIFT_H
#include <stdint.h>
static inline uint16_t __get_unaligned_be16(const uint8_t *p)
{
return p[0] << 8 | p[1];
}
static inline uint32_t __get_unaligned_be32(const uint8_t *p)
{
return p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3];
}
static inline uint64_t __get_unaligned_be64(const uint8_t *p)
{
return (uint64_t)__get_unaligned_be32(p) << 32 |
__get_unaligned_be32(p + 4);
}
static inline void __put_unaligned_be16(uint16_t val, uint8_t *p)
{
*p++ = val >> 8;
*p++ = val;
}
static inline void __put_unaligned_be32(uint32_t val, uint8_t *p)
{
__put_unaligned_be16(val >> 16, p);
__put_unaligned_be16(val, p + 2);
}
static inline void __put_unaligned_be64(uint64_t val, uint8_t *p)
{
__put_unaligned_be32(val >> 32, p);
__put_unaligned_be32(val, p + 4);
}
static inline uint16_t get_unaligned_be16(const void *p)
{
return __get_unaligned_be16((const uint8_t *)p);
}
static inline uint32_t get_unaligned_be32(const void *p)
{
return __get_unaligned_be32((const uint8_t *)p);
}
static inline uint64_t get_unaligned_be64(const void *p)
{
return __get_unaligned_be64((const uint8_t *)p);
}
static inline void put_unaligned_be16(uint16_t val, void *p)
{
__put_unaligned_be16(val, p);
}
static inline void put_unaligned_be32(uint32_t val, void *p)
{
__put_unaligned_be32(val, p);
}
static inline void put_unaligned_be64(uint64_t val, void *p)
{
__put_unaligned_be64(val, p);
}
#endif /* _TOOLS_BE_BYTESHIFT_H */

View file

@ -0,0 +1,56 @@
#ifndef _TOOLS_ENDIAN_H
#define _TOOLS_ENDIAN_H
#include <byteswap.h>
#if __BYTE_ORDER == __LITTLE_ENDIAN
#ifndef htole16
#define htole16(x) (x)
#endif
#ifndef htole32
#define htole32(x) (x)
#endif
#ifndef htole64
#define htole64(x) (x)
#endif
#ifndef le16toh
#define le16toh(x) (x)
#endif
#ifndef le32toh
#define le32toh(x) (x)
#endif
#ifndef le64toh
#define le64toh(x) (x)
#endif
#else /* __BYTE_ORDER */
#ifndef htole16
#define htole16(x) __bswap_16(x)
#endif
#ifndef htole32
#define htole32(x) __bswap_32(x)
#endif
#ifndef htole64
#define htole64(x) __bswap_64(x)
#endif
#ifndef le16toh
#define le16toh(x) __bswap_16(x)
#endif
#ifndef le32toh
#define le32toh(x) __bswap_32(x)
#endif
#ifndef le64toh
#define le64toh(x) __bswap_64(x)
#endif
#endif
#endif /* _TOOLS_ENDIAN_H */

View file

@ -0,0 +1,70 @@
#ifndef _TOOLS_LE_BYTESHIFT_H
#define _TOOLS_LE_BYTESHIFT_H
#include <stdint.h>
static inline uint16_t __get_unaligned_le16(const uint8_t *p)
{
return p[0] | p[1] << 8;
}
static inline uint32_t __get_unaligned_le32(const uint8_t *p)
{
return p[0] | p[1] << 8 | p[2] << 16 | p[3] << 24;
}
static inline uint64_t __get_unaligned_le64(const uint8_t *p)
{
return (uint64_t)__get_unaligned_le32(p + 4) << 32 |
__get_unaligned_le32(p);
}
static inline void __put_unaligned_le16(uint16_t val, uint8_t *p)
{
*p++ = val;
*p++ = val >> 8;
}
static inline void __put_unaligned_le32(uint32_t val, uint8_t *p)
{
__put_unaligned_le16(val >> 16, p + 2);
__put_unaligned_le16(val, p);
}
static inline void __put_unaligned_le64(uint64_t val, uint8_t *p)
{
__put_unaligned_le32(val >> 32, p + 4);
__put_unaligned_le32(val, p);
}
static inline uint16_t get_unaligned_le16(const void *p)
{
return __get_unaligned_le16((const uint8_t *)p);
}
static inline uint32_t get_unaligned_le32(const void *p)
{
return __get_unaligned_le32((const uint8_t *)p);
}
static inline uint64_t get_unaligned_le64(const void *p)
{
return __get_unaligned_le64((const uint8_t *)p);
}
static inline void put_unaligned_le16(uint16_t val, void *p)
{
__put_unaligned_le16(val, p);
}
static inline void put_unaligned_le32(uint32_t val, void *p)
{
__put_unaligned_le32(val, p);
}
static inline void put_unaligned_le64(uint64_t val, void *p)
{
__put_unaligned_le64(val, p);
}
#endif /* _TOOLS_LE_BYTESHIFT_H */

7
tools/lguest/Makefile Normal file
View file

@ -0,0 +1,7 @@
# This creates the demonstration utility "lguest" which runs a Linux guest.
CFLAGS:=-m32 -Wall -Wmissing-declarations -Wmissing-prototypes -O3 -U_FORTIFY_SOURCE
all: lguest
clean:
rm -f lguest

58
tools/lguest/extract Normal file
View file

@ -0,0 +1,58 @@
#! /bin/sh
set -e
PREFIX=$1
shift
trap 'rm -r $TMPDIR' 0
TMPDIR=`mktemp -d`
exec 3>/dev/null
for f; do
while IFS="
" read -r LINE; do
case "$LINE" in
*$PREFIX:[0-9]*:\**)
NUM=`echo "$LINE" | sed "s/.*$PREFIX:\([0-9]*\).*/\1/"`
if [ -f $TMPDIR/$NUM ]; then
echo "$TMPDIR/$NUM already exits prior to $f"
exit 1
fi
exec 3>>$TMPDIR/$NUM
echo $f | sed 's,\.\./,,g' > $TMPDIR/.$NUM
/bin/echo "$LINE" | sed -e "s/$PREFIX:[0-9]*//" -e "s/:\*/*/" >&3
;;
*$PREFIX:[0-9]*)
NUM=`echo "$LINE" | sed "s/.*$PREFIX:\([0-9]*\).*/\1/"`
if [ -f $TMPDIR/$NUM ]; then
echo "$TMPDIR/$NUM already exits prior to $f"
exit 1
fi
exec 3>>$TMPDIR/$NUM
echo $f | sed 's,\.\./,,g' > $TMPDIR/.$NUM
/bin/echo "$LINE" | sed "s/$PREFIX:[0-9]*//" >&3
;;
*:\**)
/bin/echo "$LINE" | sed -e "s/:\*/*/" -e "s,/\*\*/,," >&3
echo >&3
exec 3>/dev/null
;;
*)
/bin/echo "$LINE" >&3
;;
esac
done < $f
echo >&3
exec 3>/dev/null
done
LASTFILE=""
for f in $TMPDIR/*; do
if [ "$LASTFILE" != $(cat $TMPDIR/.$(basename $f) ) ]; then
LASTFILE=$(cat $TMPDIR/.$(basename $f) )
echo "[ $LASTFILE ]"
fi
cat $f
done

2072
tools/lguest/lguest.c Normal file

File diff suppressed because it is too large Load diff

125
tools/lguest/lguest.txt Normal file
View file

@ -0,0 +1,125 @@
__
(___()'`; Rusty's Remarkably Unreliable Guide to Lguest
/, /` - or, A Young Coder's Illustrated Hypervisor
\\"--\\ http://lguest.ozlabs.org
Lguest is designed to be a minimal 32-bit x86 hypervisor for the Linux kernel,
for Linux developers and users to experiment with virtualization with the
minimum of complexity. Nonetheless, it should have sufficient features to
make it useful for specific tasks, and, of course, you are encouraged to fork
and enhance it (see drivers/lguest/README).
Features:
- Kernel module which runs in a normal kernel.
- Simple I/O model for communication.
- Simple program to create new guests.
- Logo contains cute puppies: http://lguest.ozlabs.org
Developer features:
- Fun to hack on.
- No ABI: being tied to a specific kernel anyway, you can change anything.
- Many opportunities for improvement or feature implementation.
Running Lguest:
- The easiest way to run lguest is to use same kernel as guest and host.
You can configure them differently, but usually it's easiest not to.
You will need to configure your kernel with the following options:
"Processor type and features":
"Paravirtualized guest support" = Y
"Lguest guest support" = Y
"High Memory Support" = off/4GB
"Alignment value to which kernel should be aligned" = 0x100000
(CONFIG_PARAVIRT=y, CONFIG_LGUEST_GUEST=y, CONFIG_HIGHMEM64G=n and
CONFIG_PHYSICAL_ALIGN=0x100000)
"Device Drivers":
"Block devices"
"Virtio block driver" = M/Y
"Network device support"
"Universal TUN/TAP device driver support" = M/Y
"Virtio network driver" = M/Y
(CONFIG_VIRTIO_BLK=m, CONFIG_VIRTIO_NET=m and CONFIG_TUN=m)
"Virtualization"
"Linux hypervisor example code" = M/Y
(CONFIG_LGUEST=m)
- A tool called "lguest" is available in this directory: type "make"
to build it. If you didn't build your kernel in-tree, use "make
O=<builddir>".
- Create or find a root disk image. There are several useful ones
around, such as the xm-test tiny root image at
http://xm-test.xensource.com/ramdisks/initrd-1.1-i386.img
For more serious work, I usually use a distribution ISO image and
install it under qemu, then make multiple copies:
dd if=/dev/zero of=rootfile bs=1M count=2048
qemu -cdrom image.iso -hda rootfile -net user -net nic -boot d
Make sure that you install a getty on /dev/hvc0 if you want to log in on the
console!
- "modprobe lg" if you built it as a module.
- Run an lguest as root:
tools/lguest/lguest 64 vmlinux --tunnet=192.168.19.1 \
--block=rootfile root=/dev/vda
Explanation:
64: the amount of memory to use, in MB.
vmlinux: the kernel image found in the top of your build directory. You
can also use a standard bzImage.
--tunnet=192.168.19.1: configures a "tap" device for networking with this
IP address.
--block=rootfile: a file or block device which becomes /dev/vda
inside the guest.
root=/dev/vda: this (and anything else on the command line) are
kernel boot parameters.
- Configuring networking. I usually have the host masquerade, using
"iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE" and "echo 1 >
/proc/sys/net/ipv4/ip_forward". In this example, I would configure
eth0 inside the guest at 192.168.19.2.
Another method is to bridge the tap device to an external interface
using --tunnet=bridge:<bridgename>, and perhaps run dhcp on the guest
to obtain an IP address. The bridge needs to be configured first:
this option simply adds the tap interface to it.
A simple example on my system:
ifconfig eth0 0.0.0.0
brctl addbr lg0
ifconfig lg0 up
brctl addif lg0 eth0
dhclient lg0
Then use --tunnet=bridge:lg0 when launching the guest.
See:
http://www.linuxfoundation.org/collaborate/workgroups/networking/bridge
for general information on how to get bridging to work.
- Random number generation. Using the --rng option will provide a
/dev/hwrng in the guest that will read from the host's /dev/random.
Use this option in conjunction with rng-tools (see ../hw_random.txt)
to provide entropy to the guest kernel's /dev/random.
There is a helpful mailing list at http://ozlabs.org/mailman/listinfo/lguest
Good luck!
Rusty Russell rusty@rustcorp.com.au.

49
tools/lib/api/Makefile Normal file
View file

@ -0,0 +1,49 @@
include ../../scripts/Makefile.include
include ../../perf/config/utilities.mak # QUIET_CLEAN
CC = $(CROSS_COMPILE)gcc
AR = $(CROSS_COMPILE)ar
# guard against environment variables
LIB_H=
LIB_OBJS=
LIB_H += fs/debugfs.h
LIB_H += fs/fs.h
# See comment below about piggybacking...
LIB_H += fd/array.h
LIB_OBJS += $(OUTPUT)fs/debugfs.o
LIB_OBJS += $(OUTPUT)fs/fs.o
# XXX piggybacking here, need to introduce libapikfd, or rename this
# to plain libapik.a and make it have it all api goodies
LIB_OBJS += $(OUTPUT)fd/array.o
LIBFILE = libapikfs.a
CFLAGS = -ggdb3 -Wall -Wextra -std=gnu99 -Werror -O6 -D_FORTIFY_SOURCE=2 $(EXTRA_WARNINGS) $(EXTRA_CFLAGS) -fPIC
EXTLIBS = -lelf -lpthread -lrt -lm
ALL_CFLAGS = $(CFLAGS) $(BASIC_CFLAGS) -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64
ALL_LDFLAGS = $(LDFLAGS)
RM = rm -f
$(LIBFILE): $(LIB_OBJS)
$(QUIET_AR)$(RM) $@ && $(AR) rcs $(OUTPUT)$@ $(LIB_OBJS)
$(LIB_OBJS): $(LIB_H)
libapi_dirs:
$(QUIET_MKDIR)mkdir -p $(OUTPUT)fd $(OUTPUT)fs
$(OUTPUT)%.o: %.c libapi_dirs
$(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) $<
$(OUTPUT)%.s: %.c libapi_dirs
$(QUIET_CC)$(CC) -S $(ALL_CFLAGS) $<
$(OUTPUT)%.o: %.S libapi_dirs
$(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) $<
clean:
$(call QUIET_CLEAN, libapi) $(RM) $(LIB_OBJS) $(LIBFILE)
.PHONY: clean

127
tools/lib/api/fd/array.c Normal file
View file

@ -0,0 +1,127 @@
/*
* Copyright (C) 2014, Red Hat Inc, Arnaldo Carvalho de Melo <acme@redhat.com>
*
* Released under the GPL v2. (and only v2, not any later version)
*/
#include "array.h"
#include <errno.h>
#include <fcntl.h>
#include <poll.h>
#include <stdlib.h>
#include <unistd.h>
void fdarray__init(struct fdarray *fda, int nr_autogrow)
{
fda->entries = NULL;
fda->priv = NULL;
fda->nr = fda->nr_alloc = 0;
fda->nr_autogrow = nr_autogrow;
}
int fdarray__grow(struct fdarray *fda, int nr)
{
void *priv;
int nr_alloc = fda->nr_alloc + nr;
size_t psize = sizeof(fda->priv[0]) * nr_alloc;
size_t size = sizeof(struct pollfd) * nr_alloc;
struct pollfd *entries = realloc(fda->entries, size);
if (entries == NULL)
return -ENOMEM;
priv = realloc(fda->priv, psize);
if (priv == NULL) {
free(entries);
return -ENOMEM;
}
fda->nr_alloc = nr_alloc;
fda->entries = entries;
fda->priv = priv;
return 0;
}
struct fdarray *fdarray__new(int nr_alloc, int nr_autogrow)
{
struct fdarray *fda = calloc(1, sizeof(*fda));
if (fda != NULL) {
if (fdarray__grow(fda, nr_alloc)) {
free(fda);
fda = NULL;
} else {
fda->nr_autogrow = nr_autogrow;
}
}
return fda;
}
void fdarray__exit(struct fdarray *fda)
{
free(fda->entries);
free(fda->priv);
fdarray__init(fda, 0);
}
void fdarray__delete(struct fdarray *fda)
{
fdarray__exit(fda);
free(fda);
}
int fdarray__add(struct fdarray *fda, int fd, short revents)
{
int pos = fda->nr;
if (fda->nr == fda->nr_alloc &&
fdarray__grow(fda, fda->nr_autogrow) < 0)
return -ENOMEM;
fda->entries[fda->nr].fd = fd;
fda->entries[fda->nr].events = revents;
fda->nr++;
return pos;
}
int fdarray__filter(struct fdarray *fda, short revents,
void (*entry_destructor)(struct fdarray *fda, int fd))
{
int fd, nr = 0;
if (fda->nr == 0)
return 0;
for (fd = 0; fd < fda->nr; ++fd) {
if (fda->entries[fd].revents & revents) {
if (entry_destructor)
entry_destructor(fda, fd);
continue;
}
if (fd != nr) {
fda->entries[nr] = fda->entries[fd];
fda->priv[nr] = fda->priv[fd];
}
++nr;
}
return fda->nr = nr;
}
int fdarray__poll(struct fdarray *fda, int timeout)
{
return poll(fda->entries, fda->nr, timeout);
}
int fdarray__fprintf(struct fdarray *fda, FILE *fp)
{
int fd, printed = fprintf(fp, "%d [ ", fda->nr);
for (fd = 0; fd < fda->nr; ++fd)
printed += fprintf(fp, "%s%d", fd ? ", " : "", fda->entries[fd].fd);
return printed + fprintf(fp, " ]");
}

46
tools/lib/api/fd/array.h Normal file
View file

@ -0,0 +1,46 @@
#ifndef __API_FD_ARRAY__
#define __API_FD_ARRAY__
#include <stdio.h>
struct pollfd;
/**
* struct fdarray: Array of file descriptors
*
* @priv: Per array entry priv area, users should access just its contents,
* not set it to anything, as it is kept in synch with @entries, being
* realloc'ed, * for instance, in fdarray__{grow,filter}.
*
* I.e. using 'fda->priv[N].idx = * value' where N < fda->nr is ok,
* but doing 'fda->priv = malloc(M)' is not allowed.
*/
struct fdarray {
int nr;
int nr_alloc;
int nr_autogrow;
struct pollfd *entries;
union {
int idx;
} *priv;
};
void fdarray__init(struct fdarray *fda, int nr_autogrow);
void fdarray__exit(struct fdarray *fda);
struct fdarray *fdarray__new(int nr_alloc, int nr_autogrow);
void fdarray__delete(struct fdarray *fda);
int fdarray__add(struct fdarray *fda, int fd, short revents);
int fdarray__poll(struct fdarray *fda, int timeout);
int fdarray__filter(struct fdarray *fda, short revents,
void (*entry_destructor)(struct fdarray *fda, int fd));
int fdarray__grow(struct fdarray *fda, int extra);
int fdarray__fprintf(struct fdarray *fda, FILE *fp);
static inline int fdarray__available_entries(struct fdarray *fda)
{
return fda->nr_alloc - fda->nr;
}
#endif /* __API_FD_ARRAY__ */

100
tools/lib/api/fs/debugfs.c Normal file
View file

@ -0,0 +1,100 @@
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <sys/vfs.h>
#include <sys/mount.h>
#include <linux/kernel.h>
#include "debugfs.h"
char debugfs_mountpoint[PATH_MAX + 1] = "/sys/kernel/debug";
static const char * const debugfs_known_mountpoints[] = {
"/sys/kernel/debug",
"/debug",
0,
};
static bool debugfs_found;
/* find the path to the mounted debugfs */
const char *debugfs_find_mountpoint(void)
{
const char * const *ptr;
char type[100];
FILE *fp;
if (debugfs_found)
return (const char *)debugfs_mountpoint;
ptr = debugfs_known_mountpoints;
while (*ptr) {
if (debugfs_valid_mountpoint(*ptr) == 0) {
debugfs_found = true;
strcpy(debugfs_mountpoint, *ptr);
return debugfs_mountpoint;
}
ptr++;
}
/* give up and parse /proc/mounts */
fp = fopen("/proc/mounts", "r");
if (fp == NULL)
return NULL;
while (fscanf(fp, "%*s %" STR(PATH_MAX) "s %99s %*s %*d %*d\n",
debugfs_mountpoint, type) == 2) {
if (strcmp(type, "debugfs") == 0)
break;
}
fclose(fp);
if (strcmp(type, "debugfs") != 0)
return NULL;
debugfs_found = true;
return debugfs_mountpoint;
}
/* verify that a mountpoint is actually a debugfs instance */
int debugfs_valid_mountpoint(const char *debugfs)
{
struct statfs st_fs;
if (statfs(debugfs, &st_fs) < 0)
return -ENOENT;
else if (st_fs.f_type != (long) DEBUGFS_MAGIC)
return -ENOENT;
return 0;
}
/* mount the debugfs somewhere if it's not mounted */
char *debugfs_mount(const char *mountpoint)
{
/* see if it's already mounted */
if (debugfs_find_mountpoint())
goto out;
/* if not mounted and no argument */
if (mountpoint == NULL) {
/* see if environment variable set */
mountpoint = getenv(PERF_DEBUGFS_ENVIRONMENT);
/* if no environment variable, use default */
if (mountpoint == NULL)
mountpoint = "/sys/kernel/debug";
}
if (mount(NULL, mountpoint, "debugfs", 0, NULL) < 0)
return NULL;
/* save the mountpoint */
debugfs_found = true;
strncpy(debugfs_mountpoint, mountpoint, sizeof(debugfs_mountpoint));
out:
return debugfs_mountpoint;
}

View file

@ -0,0 +1,29 @@
#ifndef __API_DEBUGFS_H__
#define __API_DEBUGFS_H__
#define _STR(x) #x
#define STR(x) _STR(x)
/*
* On most systems <limits.h> would have given us this, but not on some systems
* (e.g. GNU/Hurd).
*/
#ifndef PATH_MAX
#define PATH_MAX 4096
#endif
#ifndef DEBUGFS_MAGIC
#define DEBUGFS_MAGIC 0x64626720
#endif
#ifndef PERF_DEBUGFS_ENVIRONMENT
#define PERF_DEBUGFS_ENVIRONMENT "PERF_DEBUGFS_DIR"
#endif
const char *debugfs_find_mountpoint(void);
int debugfs_valid_mountpoint(const char *debugfs);
char *debugfs_mount(const char *mountpoint);
extern char debugfs_mountpoint[];
#endif /* __API_DEBUGFS_H__ */

165
tools/lib/api/fs/fs.c Normal file
View file

@ -0,0 +1,165 @@
/* TODO merge/factor in debugfs.c here */
#include <ctype.h>
#include <errno.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/vfs.h>
#include "debugfs.h"
#include "fs.h"
static const char * const sysfs__fs_known_mountpoints[] = {
"/sys",
0,
};
static const char * const procfs__known_mountpoints[] = {
"/proc",
0,
};
struct fs {
const char *name;
const char * const *mounts;
char path[PATH_MAX + 1];
bool found;
long magic;
};
enum {
FS__SYSFS = 0,
FS__PROCFS = 1,
};
static struct fs fs__entries[] = {
[FS__SYSFS] = {
.name = "sysfs",
.mounts = sysfs__fs_known_mountpoints,
.magic = SYSFS_MAGIC,
},
[FS__PROCFS] = {
.name = "proc",
.mounts = procfs__known_mountpoints,
.magic = PROC_SUPER_MAGIC,
},
};
static bool fs__read_mounts(struct fs *fs)
{
bool found = false;
char type[100];
FILE *fp;
fp = fopen("/proc/mounts", "r");
if (fp == NULL)
return NULL;
while (!found &&
fscanf(fp, "%*s %" STR(PATH_MAX) "s %99s %*s %*d %*d\n",
fs->path, type) == 2) {
if (strcmp(type, fs->name) == 0)
found = true;
}
fclose(fp);
return fs->found = found;
}
static int fs__valid_mount(const char *fs, long magic)
{
struct statfs st_fs;
if (statfs(fs, &st_fs) < 0)
return -ENOENT;
else if (st_fs.f_type != magic)
return -ENOENT;
return 0;
}
static bool fs__check_mounts(struct fs *fs)
{
const char * const *ptr;
ptr = fs->mounts;
while (*ptr) {
if (fs__valid_mount(*ptr, fs->magic) == 0) {
fs->found = true;
strcpy(fs->path, *ptr);
return true;
}
ptr++;
}
return false;
}
static void mem_toupper(char *f, size_t len)
{
while (len) {
*f = toupper(*f);
f++;
len--;
}
}
/*
* Check for "NAME_PATH" environment variable to override fs location (for
* testing). This matches the recommendation in Documentation/sysfs-rules.txt
* for SYSFS_PATH.
*/
static bool fs__env_override(struct fs *fs)
{
char *override_path;
size_t name_len = strlen(fs->name);
/* name + "_PATH" + '\0' */
char upper_name[name_len + 5 + 1];
memcpy(upper_name, fs->name, name_len);
mem_toupper(upper_name, name_len);
strcpy(&upper_name[name_len], "_PATH");
override_path = getenv(upper_name);
if (!override_path)
return false;
fs->found = true;
strncpy(fs->path, override_path, sizeof(fs->path));
return true;
}
static const char *fs__get_mountpoint(struct fs *fs)
{
if (fs__env_override(fs))
return fs->path;
if (fs__check_mounts(fs))
return fs->path;
if (fs__read_mounts(fs))
return fs->path;
return NULL;
}
static const char *fs__mountpoint(int idx)
{
struct fs *fs = &fs__entries[idx];
if (fs->found)
return (const char *)fs->path;
return fs__get_mountpoint(fs);
}
#define FS__MOUNTPOINT(name, idx) \
const char *name##__mountpoint(void) \
{ \
return fs__mountpoint(idx); \
}
FS__MOUNTPOINT(sysfs, FS__SYSFS);
FS__MOUNTPOINT(procfs, FS__PROCFS);

14
tools/lib/api/fs/fs.h Normal file
View file

@ -0,0 +1,14 @@
#ifndef __API_FS__
#define __API_FS__
#ifndef SYSFS_MAGIC
#define SYSFS_MAGIC 0x62656572
#endif
#ifndef PROC_SUPER_MAGIC
#define PROC_SUPER_MAGIC 0x9fa0
#endif
const char *sysfs__mountpoint(void);
const char *procfs__mountpoint(void);
#endif /* __API_FS__ */

243
tools/lib/lockdep/Makefile Normal file
View file

@ -0,0 +1,243 @@
# file format version
FILE_VERSION = 1
LIBLOCKDEP_VERSION=$(shell make --no-print-directory -sC ../../.. kernelversion)
# Makefiles suck: This macro sets a default value of $(2) for the
# variable named by $(1), unless the variable has been set by
# environment or command line. This is necessary for CC and AR
# because make sets default values, so the simpler ?= approach
# won't work as expected.
define allow-override
$(if $(or $(findstring environment,$(origin $(1))),\
$(findstring command line,$(origin $(1)))),,\
$(eval $(1) = $(2)))
endef
# Allow setting CC and AR, or setting CROSS_COMPILE as a prefix.
$(call allow-override,CC,$(CROSS_COMPILE)gcc)
$(call allow-override,AR,$(CROSS_COMPILE)ar)
INSTALL = install
# Use DESTDIR for installing into a different root directory.
# This is useful for building a package. The program will be
# installed in this directory as if it was the root directory.
# Then the build tool can move it later.
DESTDIR ?=
DESTDIR_SQ = '$(subst ','\'',$(DESTDIR))'
prefix ?= /usr/local
libdir_relative = lib
libdir = $(prefix)/$(libdir_relative)
bindir_relative = bin
bindir = $(prefix)/$(bindir_relative)
export DESTDIR DESTDIR_SQ INSTALL
# copy a bit from Linux kbuild
ifeq ("$(origin V)", "command line")
VERBOSE = $(V)
endif
ifndef VERBOSE
VERBOSE = 0
endif
ifeq ("$(origin O)", "command line")
BUILD_OUTPUT := $(O)
endif
ifeq ($(BUILD_SRC),)
ifneq ($(BUILD_OUTPUT),)
define build_output
$(if $(VERBOSE:1=),@)$(MAKE) -C $(BUILD_OUTPUT) \
BUILD_SRC=$(CURDIR) -f $(CURDIR)/Makefile $1
endef
saved-output := $(BUILD_OUTPUT)
BUILD_OUTPUT := $(shell cd $(BUILD_OUTPUT) && /bin/pwd)
$(if $(BUILD_OUTPUT),, \
$(error output directory "$(saved-output)" does not exist))
all: sub-make
gui: force
$(call build_output, all_cmd)
$(filter-out gui,$(MAKECMDGOALS)): sub-make
sub-make: force
$(call build_output, $(MAKECMDGOALS))
# Leave processing to above invocation of make
skip-makefile := 1
endif # BUILD_OUTPUT
endif # BUILD_SRC
# We process the rest of the Makefile if this is the final invocation of make
ifeq ($(skip-makefile),)
srctree := $(realpath $(if $(BUILD_SRC),$(BUILD_SRC),$(CURDIR)))
objtree := $(realpath $(CURDIR))
src := $(srctree)
obj := $(objtree)
export prefix libdir bindir src obj
# Shell quotes
libdir_SQ = $(subst ','\'',$(libdir))
bindir_SQ = $(subst ','\'',$(bindir))
LIB_FILE = liblockdep.a liblockdep.so.$(LIBLOCKDEP_VERSION)
BIN_FILE = lockdep
CONFIG_INCLUDES =
CONFIG_LIBS =
CONFIG_FLAGS =
OBJ = $@
N =
export Q VERBOSE
INCLUDES = -I. -I/usr/local/include -I./uinclude -I./include -I../../include $(CONFIG_INCLUDES)
# Set compile option CFLAGS if not set elsewhere
CFLAGS ?= -g -DCONFIG_LOCKDEP -DCONFIG_STACKTRACE -DCONFIG_PROVE_LOCKING -DBITS_PER_LONG=__WORDSIZE -DLIBLOCKDEP_VERSION='"$(LIBLOCKDEP_VERSION)"' -rdynamic -O0 -g
override CFLAGS += $(CONFIG_FLAGS) $(INCLUDES) $(PLUGIN_DIR_SQ)
ifeq ($(VERBOSE),1)
Q =
print_compile =
print_app_build =
print_fpic_compile =
print_shared_lib_compile =
print_install =
else
Q = @
print_compile = echo ' CC '$(OBJ);
print_app_build = echo ' BUILD '$(OBJ);
print_fpic_compile = echo ' CC FPIC '$(OBJ);
print_shared_lib_compile = echo ' BUILD SHARED LIB '$(OBJ);
print_static_lib_build = echo ' BUILD STATIC LIB '$(OBJ);
print_install = echo ' INSTALL '$1' to $(DESTDIR_SQ)$2';
endif
do_fpic_compile = \
($(print_fpic_compile) \
$(CC) -c $(CFLAGS) $(EXT) -fPIC $< -o $@)
do_app_build = \
($(print_app_build) \
$(CC) $^ -rdynamic -o $@ $(CONFIG_LIBS) $(LIBS))
do_compile_shared_library = \
($(print_shared_lib_compile) \
$(CC) --shared $^ -o $@ -lpthread -ldl -Wl,-soname='"$@"';$(shell ln -s $@ liblockdep.so))
do_build_static_lib = \
($(print_static_lib_build) \
$(RM) $@; $(AR) rcs $@ $^)
define do_compile
$(print_compile) \
$(CC) -c $(CFLAGS) $(EXT) $< -o $(obj)/$@;
endef
$(obj)/%.o: $(src)/%.c
$(Q)$(call do_compile)
%.o: $(src)/%.c
$(Q)$(call do_compile)
PEVENT_LIB_OBJS = common.o lockdep.o preload.o rbtree.o
ALL_OBJS = $(PEVENT_LIB_OBJS)
CMD_TARGETS = $(LIB_FILE)
TARGETS = $(CMD_TARGETS)
all: all_cmd
all_cmd: $(CMD_TARGETS)
liblockdep.so.$(LIBLOCKDEP_VERSION): $(PEVENT_LIB_OBJS)
$(Q)$(do_compile_shared_library)
liblockdep.a: $(PEVENT_LIB_OBJS)
$(Q)$(do_build_static_lib)
$(PEVENT_LIB_OBJS): %.o: $(src)/%.c
$(Q)$(do_fpic_compile)
## make deps
all_objs := $(sort $(ALL_OBJS))
all_deps := $(all_objs:%.o=.%.d)
# let .d file also depends on the source and header files
define check_deps
@set -e; $(RM) $@; \
$(CC) -MM $(CFLAGS) $< > $@.$$$$; \
sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \
$(RM) $@.$$$$
endef
$(all_deps): .%.d: $(src)/%.c
$(Q)$(call check_deps)
$(all_objs) : %.o : .%.d
dep_includes := $(wildcard $(all_deps))
ifneq ($(dep_includes),)
include $(dep_includes)
endif
### Detect environment changes
TRACK_CFLAGS = $(subst ','\'',$(CFLAGS)):$(ARCH):$(CROSS_COMPILE)
tags: force
$(RM) tags
find . -name '*.[ch]' | xargs ctags --extra=+f --c-kinds=+px \
--regex-c++='/_PE\(([^,)]*).*/PEVENT_ERRNO__\1/'
TAGS: force
$(RM) TAGS
find . -name '*.[ch]' | xargs etags \
--regex='/_PE(\([^,)]*\).*/PEVENT_ERRNO__\1/'
define do_install
$(print_install) \
if [ ! -d '$(DESTDIR_SQ)$2' ]; then \
$(INSTALL) -d -m 755 '$(DESTDIR_SQ)$2'; \
fi; \
$(INSTALL) $1 '$(DESTDIR_SQ)$2'
endef
install_lib: all_cmd
$(Q)$(call do_install,$(LIB_FILE),$(libdir_SQ))
$(Q)$(call do_install,$(BIN_FILE),$(bindir_SQ))
install: install_lib
clean:
$(RM) *.o *~ $(TARGETS) *.a *liblockdep*.so* $(VERSION_FILES) .*.d
$(RM) tags TAGS
endif # skip-makefile
PHONY += force
force:
# Declare the contents of the .PHONY variable as phony. We keep that
# information in a variable so we can use it in if_changed and friends.
.PHONY: $(PHONY)

View file

@ -0,0 +1,33 @@
#include <stddef.h>
#include <stdbool.h>
#include <linux/compiler.h>
#include <linux/lockdep.h>
#include <unistd.h>
#include <sys/syscall.h>
static __thread struct task_struct current_obj;
/* lockdep wants these */
bool debug_locks = true;
bool debug_locks_silent;
__attribute__((constructor)) static void liblockdep_init(void)
{
lockdep_init();
}
__attribute__((destructor)) static void liblockdep_exit(void)
{
debug_check_no_locks_held(&current_obj);
}
struct task_struct *__curr(void)
{
if (current_obj.pid == 0) {
/* Makes lockdep output pretty */
prctl(PR_GET_NAME, current_obj.comm);
current_obj.pid = syscall(__NR_gettid);
}
return &current_obj;
}

View file

@ -0,0 +1,50 @@
#ifndef _LIBLOCKDEP_COMMON_H
#define _LIBLOCKDEP_COMMON_H
#include <pthread.h>
#define NR_LOCKDEP_CACHING_CLASSES 2
#define MAX_LOCKDEP_SUBCLASSES 8UL
#ifndef CALLER_ADDR0
#define CALLER_ADDR0 ((unsigned long)__builtin_return_address(0))
#endif
#ifndef _RET_IP_
#define _RET_IP_ CALLER_ADDR0
#endif
#ifndef _THIS_IP_
#define _THIS_IP_ ({ __label__ __here; __here: (unsigned long)&&__here; })
#endif
struct lockdep_subclass_key {
char __one_byte;
};
struct lock_class_key {
struct lockdep_subclass_key subkeys[MAX_LOCKDEP_SUBCLASSES];
};
struct lockdep_map {
struct lock_class_key *key;
struct lock_class *class_cache[NR_LOCKDEP_CACHING_CLASSES];
const char *name;
#ifdef CONFIG_LOCK_STAT
int cpu;
unsigned long ip;
#endif
};
void lockdep_init_map(struct lockdep_map *lock, const char *name,
struct lock_class_key *key, int subclass);
void lock_acquire(struct lockdep_map *lock, unsigned int subclass,
int trylock, int read, int check,
struct lockdep_map *nest_lock, unsigned long ip);
void lock_release(struct lockdep_map *lock, int nested,
unsigned long ip);
#define STATIC_LOCKDEP_MAP_INIT(_name, _key) \
{ .name = (_name), .key = (void *)(_key), }
#endif

View file

@ -0,0 +1,70 @@
#ifndef _LIBLOCKDEP_MUTEX_H
#define _LIBLOCKDEP_MUTEX_H
#include <pthread.h>
#include "common.h"
struct liblockdep_pthread_mutex {
pthread_mutex_t mutex;
struct lockdep_map dep_map;
};
typedef struct liblockdep_pthread_mutex liblockdep_pthread_mutex_t;
#define LIBLOCKDEP_PTHREAD_MUTEX_INITIALIZER(mtx) \
(const struct liblockdep_pthread_mutex) { \
.mutex = PTHREAD_MUTEX_INITIALIZER, \
.dep_map = STATIC_LOCKDEP_MAP_INIT(#mtx, &((&(mtx))->dep_map)), \
}
static inline int __mutex_init(liblockdep_pthread_mutex_t *lock,
const char *name,
struct lock_class_key *key,
const pthread_mutexattr_t *__mutexattr)
{
lockdep_init_map(&lock->dep_map, name, key, 0);
return pthread_mutex_init(&lock->mutex, __mutexattr);
}
#define liblockdep_pthread_mutex_init(mutex, mutexattr) \
({ \
static struct lock_class_key __key; \
\
__mutex_init((mutex), #mutex, &__key, (mutexattr)); \
})
static inline int liblockdep_pthread_mutex_lock(liblockdep_pthread_mutex_t *lock)
{
lock_acquire(&lock->dep_map, 0, 0, 0, 1, NULL, (unsigned long)_RET_IP_);
return pthread_mutex_lock(&lock->mutex);
}
static inline int liblockdep_pthread_mutex_unlock(liblockdep_pthread_mutex_t *lock)
{
lock_release(&lock->dep_map, 0, (unsigned long)_RET_IP_);
return pthread_mutex_unlock(&lock->mutex);
}
static inline int liblockdep_pthread_mutex_trylock(liblockdep_pthread_mutex_t *lock)
{
lock_acquire(&lock->dep_map, 0, 1, 0, 1, NULL, (unsigned long)_RET_IP_);
return pthread_mutex_trylock(&lock->mutex) == 0 ? 1 : 0;
}
static inline int liblockdep_pthread_mutex_destroy(liblockdep_pthread_mutex_t *lock)
{
return pthread_mutex_destroy(&lock->mutex);
}
#ifdef __USE_LIBLOCKDEP
#define pthread_mutex_t liblockdep_pthread_mutex_t
#define pthread_mutex_init liblockdep_pthread_mutex_init
#define pthread_mutex_lock liblockdep_pthread_mutex_lock
#define pthread_mutex_unlock liblockdep_pthread_mutex_unlock
#define pthread_mutex_trylock liblockdep_pthread_mutex_trylock
#define pthread_mutex_destroy liblockdep_pthread_mutex_destroy
#endif
#endif

View file

@ -0,0 +1,86 @@
#ifndef _LIBLOCKDEP_RWLOCK_H
#define _LIBLOCKDEP_RWLOCK_H
#include <pthread.h>
#include "common.h"
struct liblockdep_pthread_rwlock {
pthread_rwlock_t rwlock;
struct lockdep_map dep_map;
};
typedef struct liblockdep_pthread_rwlock liblockdep_pthread_rwlock_t;
#define LIBLOCKDEP_PTHREAD_RWLOCK_INITIALIZER(rwl) \
(struct liblockdep_pthread_rwlock) { \
.rwlock = PTHREAD_RWLOCK_INITIALIZER, \
.dep_map = STATIC_LOCKDEP_MAP_INIT(#rwl, &((&(rwl))->dep_map)), \
}
static inline int __rwlock_init(liblockdep_pthread_rwlock_t *lock,
const char *name,
struct lock_class_key *key,
const pthread_rwlockattr_t *attr)
{
lockdep_init_map(&lock->dep_map, name, key, 0);
return pthread_rwlock_init(&lock->rwlock, attr);
}
#define liblockdep_pthread_rwlock_init(lock, attr) \
({ \
static struct lock_class_key __key; \
\
__rwlock_init((lock), #lock, &__key, (attr)); \
})
static inline int liblockdep_pthread_rwlock_rdlock(liblockdep_pthread_rwlock_t *lock)
{
lock_acquire(&lock->dep_map, 0, 0, 2, 1, NULL, (unsigned long)_RET_IP_);
return pthread_rwlock_rdlock(&lock->rwlock);
}
static inline int liblockdep_pthread_rwlock_unlock(liblockdep_pthread_rwlock_t *lock)
{
lock_release(&lock->dep_map, 0, (unsigned long)_RET_IP_);
return pthread_rwlock_unlock(&lock->rwlock);
}
static inline int liblockdep_pthread_rwlock_wrlock(liblockdep_pthread_rwlock_t *lock)
{
lock_acquire(&lock->dep_map, 0, 0, 0, 1, NULL, (unsigned long)_RET_IP_);
return pthread_rwlock_wrlock(&lock->rwlock);
}
static inline int liblockdep_pthread_rwlock_tryrdlock(liblockdep_pthread_rwlock_t *lock)
{
lock_acquire(&lock->dep_map, 0, 1, 2, 1, NULL, (unsigned long)_RET_IP_);
return pthread_rwlock_tryrdlock(&lock->rwlock) == 0 ? 1 : 0;
}
static inline int liblockdep_pthread_rwlock_trywlock(liblockdep_pthread_rwlock_t *lock)
{
lock_acquire(&lock->dep_map, 0, 1, 0, 1, NULL, (unsigned long)_RET_IP_);
return pthread_rwlock_trywlock(&lock->rwlock) == 0 ? 1 : 0;
}
static inline int liblockdep_rwlock_destroy(liblockdep_pthread_rwlock_t *lock)
{
return pthread_rwlock_destroy(&lock->rwlock);
}
#ifdef __USE_LIBLOCKDEP
#define pthread_rwlock_t liblockdep_pthread_rwlock_t
#define pthread_rwlock_init liblockdep_pthread_rwlock_init
#define pthread_rwlock_rdlock liblockdep_pthread_rwlock_rdlock
#define pthread_rwlock_unlock liblockdep_pthread_rwlock_unlock
#define pthread_rwlock_wrlock liblockdep_pthread_rwlock_wrlock
#define pthread_rwlock_tryrdlock liblockdep_pthread_rwlock_tryrdlock
#define pthread_rwlock_trywlock liblockdep_pthread_rwlock_trywlock
#define pthread_rwlock_destroy liblockdep_rwlock_destroy
#endif
#endif

3
tools/lib/lockdep/lockdep Executable file
View file

@ -0,0 +1,3 @@
#!/bin/bash
LD_PRELOAD="./liblockdep.so $LD_PRELOAD" "$@"

View file

@ -0,0 +1,2 @@
#include <linux/lockdep.h>
#include "../../../kernel/locking/lockdep.c"

View file

@ -0,0 +1 @@
#include "../../../kernel/locking/lockdep_internals.h"

View file

@ -0,0 +1 @@
#include "../../../kernel/locking/lockdep_states.h"

445
tools/lib/lockdep/preload.c Normal file
View file

@ -0,0 +1,445 @@
#define _GNU_SOURCE
#include <pthread.h>
#include <stdio.h>
#include <dlfcn.h>
#include <stdlib.h>
#include <sysexits.h>
#include "include/liblockdep/mutex.h"
#include "../../../include/linux/rbtree.h"
/**
* struct lock_lookup - liblockdep's view of a single unique lock
* @orig: pointer to the original pthread lock, used for lookups
* @dep_map: lockdep's dep_map structure
* @key: lockdep's key structure
* @node: rb-tree node used to store the lock in a global tree
* @name: a unique name for the lock
*/
struct lock_lookup {
void *orig; /* Original pthread lock, used for lookups */
struct lockdep_map dep_map; /* Since all locks are dynamic, we need
* a dep_map and a key for each lock */
/*
* Wait, there's no support for key classes? Yup :(
* Most big projects wrap the pthread api with their own calls to
* be compatible with different locking methods. This means that
* "classes" will be brokes since the function that creates all
* locks will point to a generic locking function instead of the
* actual code that wants to do the locking.
*/
struct lock_class_key key;
struct rb_node node;
#define LIBLOCKDEP_MAX_LOCK_NAME 22
char name[LIBLOCKDEP_MAX_LOCK_NAME];
};
/* This is where we store our locks */
static struct rb_root locks = RB_ROOT;
static pthread_rwlock_t locks_rwlock = PTHREAD_RWLOCK_INITIALIZER;
/* pthread mutex API */
#ifdef __GLIBC__
extern int __pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr);
extern int __pthread_mutex_lock(pthread_mutex_t *mutex);
extern int __pthread_mutex_trylock(pthread_mutex_t *mutex);
extern int __pthread_mutex_unlock(pthread_mutex_t *mutex);
extern int __pthread_mutex_destroy(pthread_mutex_t *mutex);
#else
#define __pthread_mutex_init NULL
#define __pthread_mutex_lock NULL
#define __pthread_mutex_trylock NULL
#define __pthread_mutex_unlock NULL
#define __pthread_mutex_destroy NULL
#endif
static int (*ll_pthread_mutex_init)(pthread_mutex_t *mutex,
const pthread_mutexattr_t *attr) = __pthread_mutex_init;
static int (*ll_pthread_mutex_lock)(pthread_mutex_t *mutex) = __pthread_mutex_lock;
static int (*ll_pthread_mutex_trylock)(pthread_mutex_t *mutex) = __pthread_mutex_trylock;
static int (*ll_pthread_mutex_unlock)(pthread_mutex_t *mutex) = __pthread_mutex_unlock;
static int (*ll_pthread_mutex_destroy)(pthread_mutex_t *mutex) = __pthread_mutex_destroy;
/* pthread rwlock API */
#ifdef __GLIBC__
extern int __pthread_rwlock_init(pthread_rwlock_t *rwlock, const pthread_rwlockattr_t *attr);
extern int __pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
extern int __pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
extern int __pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);
extern int __pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
extern int __pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);
extern int __pthread_rwlock_unlock(pthread_rwlock_t *rwlock);
#else
#define __pthread_rwlock_init NULL
#define __pthread_rwlock_destroy NULL
#define __pthread_rwlock_wrlock NULL
#define __pthread_rwlock_trywrlock NULL
#define __pthread_rwlock_rdlock NULL
#define __pthread_rwlock_tryrdlock NULL
#define __pthread_rwlock_unlock NULL
#endif
static int (*ll_pthread_rwlock_init)(pthread_rwlock_t *rwlock,
const pthread_rwlockattr_t *attr) = __pthread_rwlock_init;
static int (*ll_pthread_rwlock_destroy)(pthread_rwlock_t *rwlock) = __pthread_rwlock_destroy;
static int (*ll_pthread_rwlock_rdlock)(pthread_rwlock_t *rwlock) = __pthread_rwlock_rdlock;
static int (*ll_pthread_rwlock_tryrdlock)(pthread_rwlock_t *rwlock) = __pthread_rwlock_tryrdlock;
static int (*ll_pthread_rwlock_trywrlock)(pthread_rwlock_t *rwlock) = __pthread_rwlock_trywrlock;
static int (*ll_pthread_rwlock_wrlock)(pthread_rwlock_t *rwlock) = __pthread_rwlock_wrlock;
static int (*ll_pthread_rwlock_unlock)(pthread_rwlock_t *rwlock) = __pthread_rwlock_unlock;
enum { none, prepare, done, } __init_state;
static void init_preload(void);
static void try_init_preload(void)
{
if (__init_state != done)
init_preload();
}
static struct rb_node **__get_lock_node(void *lock, struct rb_node **parent)
{
struct rb_node **node = &locks.rb_node;
struct lock_lookup *l;
*parent = NULL;
while (*node) {
l = rb_entry(*node, struct lock_lookup, node);
*parent = *node;
if (lock < l->orig)
node = &l->node.rb_left;
else if (lock > l->orig)
node = &l->node.rb_right;
else
return node;
}
return node;
}
#ifndef LIBLOCKDEP_STATIC_ENTRIES
#define LIBLOCKDEP_STATIC_ENTRIES 1024
#endif
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
static struct lock_lookup __locks[LIBLOCKDEP_STATIC_ENTRIES];
static int __locks_nr;
static inline bool is_static_lock(struct lock_lookup *lock)
{
return lock >= __locks && lock < __locks + ARRAY_SIZE(__locks);
}
static struct lock_lookup *alloc_lock(void)
{
if (__init_state != done) {
/*
* Some programs attempt to initialize and use locks in their
* allocation path. This means that a call to malloc() would
* result in locks being initialized and locked.
*
* Why is it an issue for us? dlsym() below will try allocating
* to give us the original function. Since this allocation will
* result in a locking operations, we have to let pthread deal
* with it, but we can't! we don't have the pointer to the
* original API since we're inside dlsym() trying to get it
*/
int idx = __locks_nr++;
if (idx >= ARRAY_SIZE(__locks)) {
fprintf(stderr,
"LOCKDEP error: insufficient LIBLOCKDEP_STATIC_ENTRIES\n");
exit(EX_UNAVAILABLE);
}
return __locks + idx;
}
return malloc(sizeof(struct lock_lookup));
}
static inline void free_lock(struct lock_lookup *lock)
{
if (likely(!is_static_lock(lock)))
free(lock);
}
/**
* __get_lock - find or create a lock instance
* @lock: pointer to a pthread lock function
*
* Try to find an existing lock in the rbtree using the provided pointer. If
* one wasn't found - create it.
*/
static struct lock_lookup *__get_lock(void *lock)
{
struct rb_node **node, *parent;
struct lock_lookup *l;
ll_pthread_rwlock_rdlock(&locks_rwlock);
node = __get_lock_node(lock, &parent);
ll_pthread_rwlock_unlock(&locks_rwlock);
if (*node) {
return rb_entry(*node, struct lock_lookup, node);
}
/* We didn't find the lock, let's create it */
l = alloc_lock();
if (l == NULL)
return NULL;
l->orig = lock;
/*
* Currently the name of the lock is the ptr value of the pthread lock,
* while not optimal, it makes debugging a bit easier.
*
* TODO: Get the real name of the lock using libdwarf
*/
sprintf(l->name, "%p", lock);
lockdep_init_map(&l->dep_map, l->name, &l->key, 0);
ll_pthread_rwlock_wrlock(&locks_rwlock);
/* This might have changed since the last time we fetched it */
node = __get_lock_node(lock, &parent);
rb_link_node(&l->node, parent, node);
rb_insert_color(&l->node, &locks);
ll_pthread_rwlock_unlock(&locks_rwlock);
return l;
}
static void __del_lock(struct lock_lookup *lock)
{
ll_pthread_rwlock_wrlock(&locks_rwlock);
rb_erase(&lock->node, &locks);
ll_pthread_rwlock_unlock(&locks_rwlock);
free_lock(lock);
}
int pthread_mutex_init(pthread_mutex_t *mutex,
const pthread_mutexattr_t *attr)
{
int r;
/*
* We keep trying to init our preload module because there might be
* code in init sections that tries to touch locks before we are
* initialized, in that case we'll need to manually call preload
* to get us going.
*
* Funny enough, kernel's lockdep had the same issue, and used
* (almost) the same solution. See look_up_lock_class() in
* kernel/locking/lockdep.c for details.
*/
try_init_preload();
r = ll_pthread_mutex_init(mutex, attr);
if (r == 0)
/*
* We do a dummy initialization here so that lockdep could
* warn us if something fishy is going on - such as
* initializing a held lock.
*/
__get_lock(mutex);
return r;
}
int pthread_mutex_lock(pthread_mutex_t *mutex)
{
int r;
try_init_preload();
lock_acquire(&__get_lock(mutex)->dep_map, 0, 0, 0, 1, NULL,
(unsigned long)_RET_IP_);
/*
* Here's the thing with pthread mutexes: unlike the kernel variant,
* they can fail.
*
* This means that the behaviour here is a bit different from what's
* going on in the kernel: there we just tell lockdep that we took the
* lock before actually taking it, but here we must deal with the case
* that locking failed.
*
* To do that we'll "release" the lock if locking failed - this way
* we'll get lockdep doing the correct checks when we try to take
* the lock, and if that fails - we'll be back to the correct
* state by releasing it.
*/
r = ll_pthread_mutex_lock(mutex);
if (r)
lock_release(&__get_lock(mutex)->dep_map, 0, (unsigned long)_RET_IP_);
return r;
}
int pthread_mutex_trylock(pthread_mutex_t *mutex)
{
int r;
try_init_preload();
lock_acquire(&__get_lock(mutex)->dep_map, 0, 1, 0, 1, NULL, (unsigned long)_RET_IP_);
r = ll_pthread_mutex_trylock(mutex);
if (r)
lock_release(&__get_lock(mutex)->dep_map, 0, (unsigned long)_RET_IP_);
return r;
}
int pthread_mutex_unlock(pthread_mutex_t *mutex)
{
int r;
try_init_preload();
lock_release(&__get_lock(mutex)->dep_map, 0, (unsigned long)_RET_IP_);
/*
* Just like taking a lock, only in reverse!
*
* If we fail releasing the lock, tell lockdep we're holding it again.
*/
r = ll_pthread_mutex_unlock(mutex);
if (r)
lock_acquire(&__get_lock(mutex)->dep_map, 0, 0, 0, 1, NULL, (unsigned long)_RET_IP_);
return r;
}
int pthread_mutex_destroy(pthread_mutex_t *mutex)
{
try_init_preload();
/*
* Let's see if we're releasing a lock that's held.
*
* TODO: Hook into free() and add that check there as well.
*/
debug_check_no_locks_freed(mutex, mutex + sizeof(*mutex));
__del_lock(__get_lock(mutex));
return ll_pthread_mutex_destroy(mutex);
}
/* This is the rwlock part, very similar to what happened with mutex above */
int pthread_rwlock_init(pthread_rwlock_t *rwlock,
const pthread_rwlockattr_t *attr)
{
int r;
try_init_preload();
r = ll_pthread_rwlock_init(rwlock, attr);
if (r == 0)
__get_lock(rwlock);
return r;
}
int pthread_rwlock_destroy(pthread_rwlock_t *rwlock)
{
try_init_preload();
debug_check_no_locks_freed(rwlock, rwlock + sizeof(*rwlock));
__del_lock(__get_lock(rwlock));
return ll_pthread_rwlock_destroy(rwlock);
}
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock)
{
int r;
init_preload();
lock_acquire(&__get_lock(rwlock)->dep_map, 0, 0, 2, 1, NULL, (unsigned long)_RET_IP_);
r = ll_pthread_rwlock_rdlock(rwlock);
if (r)
lock_release(&__get_lock(rwlock)->dep_map, 0, (unsigned long)_RET_IP_);
return r;
}
int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock)
{
int r;
init_preload();
lock_acquire(&__get_lock(rwlock)->dep_map, 0, 1, 2, 1, NULL, (unsigned long)_RET_IP_);
r = ll_pthread_rwlock_tryrdlock(rwlock);
if (r)
lock_release(&__get_lock(rwlock)->dep_map, 0, (unsigned long)_RET_IP_);
return r;
}
int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock)
{
int r;
init_preload();
lock_acquire(&__get_lock(rwlock)->dep_map, 0, 1, 0, 1, NULL, (unsigned long)_RET_IP_);
r = ll_pthread_rwlock_trywrlock(rwlock);
if (r)
lock_release(&__get_lock(rwlock)->dep_map, 0, (unsigned long)_RET_IP_);
return r;
}
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock)
{
int r;
init_preload();
lock_acquire(&__get_lock(rwlock)->dep_map, 0, 0, 0, 1, NULL, (unsigned long)_RET_IP_);
r = ll_pthread_rwlock_wrlock(rwlock);
if (r)
lock_release(&__get_lock(rwlock)->dep_map, 0, (unsigned long)_RET_IP_);
return r;
}
int pthread_rwlock_unlock(pthread_rwlock_t *rwlock)
{
int r;
init_preload();
lock_release(&__get_lock(rwlock)->dep_map, 0, (unsigned long)_RET_IP_);
r = ll_pthread_rwlock_unlock(rwlock);
if (r)
lock_acquire(&__get_lock(rwlock)->dep_map, 0, 0, 0, 1, NULL, (unsigned long)_RET_IP_);
return r;
}
__attribute__((constructor)) static void init_preload(void)
{
if (__init_state == done)
return;
#ifndef __GLIBC__
__init_state = prepare;
ll_pthread_mutex_init = dlsym(RTLD_NEXT, "pthread_mutex_init");
ll_pthread_mutex_lock = dlsym(RTLD_NEXT, "pthread_mutex_lock");
ll_pthread_mutex_trylock = dlsym(RTLD_NEXT, "pthread_mutex_trylock");
ll_pthread_mutex_unlock = dlsym(RTLD_NEXT, "pthread_mutex_unlock");
ll_pthread_mutex_destroy = dlsym(RTLD_NEXT, "pthread_mutex_destroy");
ll_pthread_rwlock_init = dlsym(RTLD_NEXT, "pthread_rwlock_init");
ll_pthread_rwlock_destroy = dlsym(RTLD_NEXT, "pthread_rwlock_destroy");
ll_pthread_rwlock_rdlock = dlsym(RTLD_NEXT, "pthread_rwlock_rdlock");
ll_pthread_rwlock_tryrdlock = dlsym(RTLD_NEXT, "pthread_rwlock_tryrdlock");
ll_pthread_rwlock_wrlock = dlsym(RTLD_NEXT, "pthread_rwlock_wrlock");
ll_pthread_rwlock_trywrlock = dlsym(RTLD_NEXT, "pthread_rwlock_trywrlock");
ll_pthread_rwlock_unlock = dlsym(RTLD_NEXT, "pthread_rwlock_unlock");
#endif
lockdep_init();
__init_state = done;
}

View file

@ -0,0 +1 @@
#include "../../../lib/rbtree.c"

27
tools/lib/lockdep/run_tests.sh Executable file
View file

@ -0,0 +1,27 @@
#! /bin/bash
make &> /dev/null
for i in `ls tests/*.c`; do
testname=$(basename -s .c "$i")
gcc -o tests/$testname -pthread -lpthread $i liblockdep.a -Iinclude -D__USE_LIBLOCKDEP &> /dev/null
echo -ne "$testname... "
if [ $(timeout 1 ./tests/$testname | wc -l) -gt 0 ]; then
echo "PASSED!"
else
echo "FAILED!"
fi
rm tests/$testname
done
for i in `ls tests/*.c`; do
testname=$(basename -s .c "$i")
gcc -o tests/$testname -pthread -lpthread -Iinclude $i &> /dev/null
echo -ne "(PRELOAD) $testname... "
if [ $(timeout 1 ./lockdep ./tests/$testname | wc -l) -gt 0 ]; then
echo "PASSED!"
else
echo "FAILED!"
fi
rm tests/$testname
done

View file

@ -0,0 +1,13 @@
#include <liblockdep/mutex.h>
void main(void)
{
pthread_mutex_t a, b;
pthread_mutex_init(&a, NULL);
pthread_mutex_init(&b, NULL);
pthread_mutex_lock(&a);
pthread_mutex_lock(&b);
pthread_mutex_lock(&a);
}

View file

@ -0,0 +1,13 @@
#include <liblockdep/mutex.h>
#include "common.h"
void main(void)
{
pthread_mutex_t a, b;
pthread_mutex_init(&a, NULL);
pthread_mutex_init(&b, NULL);
LOCK_UNLOCK_2(a, b);
LOCK_UNLOCK_2(b, a);
}

View file

@ -0,0 +1,15 @@
#include <liblockdep/mutex.h>
#include "common.h"
void main(void)
{
pthread_mutex_t a, b, c;
pthread_mutex_init(&a, NULL);
pthread_mutex_init(&b, NULL);
pthread_mutex_init(&c, NULL);
LOCK_UNLOCK_2(a, b);
LOCK_UNLOCK_2(b, c);
LOCK_UNLOCK_2(c, a);
}

View file

@ -0,0 +1,17 @@
#include <liblockdep/mutex.h>
#include "common.h"
void main(void)
{
pthread_mutex_t a, b, c, d;
pthread_mutex_init(&a, NULL);
pthread_mutex_init(&b, NULL);
pthread_mutex_init(&c, NULL);
pthread_mutex_init(&d, NULL);
LOCK_UNLOCK_2(a, b);
LOCK_UNLOCK_2(b, c);
LOCK_UNLOCK_2(c, d);
LOCK_UNLOCK_2(d, a);
}

View file

@ -0,0 +1,15 @@
#include <liblockdep/mutex.h>
#include "common.h"
void main(void)
{
pthread_mutex_t a, b, c;
pthread_mutex_init(&a, NULL);
pthread_mutex_init(&b, NULL);
pthread_mutex_init(&c, NULL);
LOCK_UNLOCK_2(a, b);
LOCK_UNLOCK_2(c, a);
LOCK_UNLOCK_2(b, c);
}

View file

@ -0,0 +1,17 @@
#include <liblockdep/mutex.h>
#include "common.h"
void main(void)
{
pthread_mutex_t a, b, c, d;
pthread_mutex_init(&a, NULL);
pthread_mutex_init(&b, NULL);
pthread_mutex_init(&c, NULL);
pthread_mutex_init(&d, NULL);
LOCK_UNLOCK_2(a, b);
LOCK_UNLOCK_2(c, d);
LOCK_UNLOCK_2(b, c);
LOCK_UNLOCK_2(d, a);
}

View file

@ -0,0 +1,17 @@
#include <liblockdep/mutex.h>
#include "common.h"
void main(void)
{
pthread_mutex_t a, b, c, d;
pthread_mutex_init(&a, NULL);
pthread_mutex_init(&b, NULL);
pthread_mutex_init(&c, NULL);
pthread_mutex_init(&d, NULL);
LOCK_UNLOCK_2(a, b);
LOCK_UNLOCK_2(c, d);
LOCK_UNLOCK_2(b, d);
LOCK_UNLOCK_2(d, a);
}

View file

@ -0,0 +1,13 @@
#include <liblockdep/rwlock.h>
void main(void)
{
pthread_rwlock_t a, b;
pthread_rwlock_init(&a, NULL);
pthread_rwlock_init(&b, NULL);
pthread_rwlock_wrlock(&a);
pthread_rwlock_rdlock(&b);
pthread_rwlock_wrlock(&a);
}

View file

@ -0,0 +1,12 @@
#ifndef _LIBLOCKDEP_TEST_COMMON_H
#define _LIBLOCKDEP_TEST_COMMON_H
#define LOCK_UNLOCK_2(a, b) \
do { \
pthread_mutex_lock(&(a)); \
pthread_mutex_lock(&(b)); \
pthread_mutex_unlock(&(b)); \
pthread_mutex_unlock(&(a)); \
} while(0)
#endif

View file

@ -0,0 +1,12 @@
#include <liblockdep/mutex.h>
void main(void)
{
pthread_mutex_t a;
pthread_mutex_init(&a, NULL);
pthread_mutex_lock(&a);
pthread_mutex_unlock(&a);
pthread_mutex_unlock(&a);
}

View file

@ -0,0 +1,6 @@
#ifndef __ASM_GENERIC_HASH_H
#define __ASM_GENERIC_HASH_H
/* Stub */
#endif /* __ASM_GENERIC_HASH_H */

View file

@ -0,0 +1,3 @@
/* empty file */

View file

@ -0,0 +1,3 @@
/* empty file */

View file

@ -0,0 +1,3 @@
/* empty file */

View file

@ -0,0 +1,7 @@
#ifndef _LIBLOCKDEP_LINUX_COMPILER_H_
#define _LIBLOCKDEP_LINUX_COMPILER_H_
#define __used __attribute__((__unused__))
#define unlikely
#endif

View file

@ -0,0 +1,12 @@
#ifndef _LIBLOCKDEP_DEBUG_LOCKS_H_
#define _LIBLOCKDEP_DEBUG_LOCKS_H_
#include <stddef.h>
#include <linux/compiler.h>
#define DEBUG_LOCKS_WARN_ON(x) (x)
extern bool debug_locks;
extern bool debug_locks_silent;
#endif

View file

@ -0,0 +1,3 @@
/* empty file */

View file

@ -0,0 +1,3 @@
/* empty file */

View file

@ -0,0 +1,3 @@
/* empty file */

View file

@ -0,0 +1,11 @@
#ifndef _LIBLOCKDEP_LINUX_HARDIRQ_H_
#define _LIBLOCKDEP_LINUX_HARDIRQ_H_
#define SOFTIRQ_BITS 0UL
#define HARDIRQ_BITS 0UL
#define SOFTIRQ_SHIFT 0UL
#define HARDIRQ_SHIFT 0UL
#define hardirq_count() 0UL
#define softirq_count() 0UL
#endif

View file

@ -0,0 +1 @@
#include "../../../include/linux/hash.h"

View file

@ -0,0 +1,3 @@
/* empty file */

View file

@ -0,0 +1,38 @@
#ifndef _LIBLOCKDEP_LINUX_TRACE_IRQFLAGS_H_
#define _LIBLOCKDEP_LINUX_TRACE_IRQFLAGS_H_
# define trace_hardirq_context(p) 0
# define trace_softirq_context(p) 0
# define trace_hardirqs_enabled(p) 0
# define trace_softirqs_enabled(p) 0
# define trace_hardirq_enter() do { } while (0)
# define trace_hardirq_exit() do { } while (0)
# define lockdep_softirq_enter() do { } while (0)
# define lockdep_softirq_exit() do { } while (0)
# define INIT_TRACE_IRQFLAGS
# define stop_critical_timings() do { } while (0)
# define start_critical_timings() do { } while (0)
#define raw_local_irq_disable() do { } while (0)
#define raw_local_irq_enable() do { } while (0)
#define raw_local_irq_save(flags) ((flags) = 0)
#define raw_local_irq_restore(flags) do { } while (0)
#define raw_local_save_flags(flags) ((flags) = 0)
#define raw_irqs_disabled_flags(flags) do { } while (0)
#define raw_irqs_disabled() 0
#define raw_safe_halt()
#define local_irq_enable() do { } while (0)
#define local_irq_disable() do { } while (0)
#define local_irq_save(flags) ((flags) = 0)
#define local_irq_restore(flags) do { } while (0)
#define local_save_flags(flags) ((flags) = 0)
#define irqs_disabled() (1)
#define irqs_disabled_flags(flags) (0)
#define safe_halt() do { } while (0)
#define trace_lock_release(x, y)
#define trace_lock_acquire(a, b, c, d, e, f, g)
#endif

View file

@ -0,0 +1,32 @@
#ifndef _LIBLOCKDEP_LINUX_KALLSYMS_H_
#define _LIBLOCKDEP_LINUX_KALLSYMS_H_
#include <linux/kernel.h>
#include <stdio.h>
#define KSYM_NAME_LEN 128
struct module;
static inline const char *kallsyms_lookup(unsigned long addr,
unsigned long *symbolsize,
unsigned long *offset,
char **modname, char *namebuf)
{
return NULL;
}
#include <execinfo.h>
#include <stdlib.h>
static inline void print_ip_sym(unsigned long ip)
{
char **name;
name = backtrace_symbols((void **)&ip, 1);
printf("%s\n", *name);
free(name);
}
#endif

View file

@ -0,0 +1,25 @@
#ifndef __KERN_LEVELS_H__
#define __KERN_LEVELS_H__
#define KERN_SOH "" /* ASCII Start Of Header */
#define KERN_SOH_ASCII ''
#define KERN_EMERG KERN_SOH "" /* system is unusable */
#define KERN_ALERT KERN_SOH "" /* action must be taken immediately */
#define KERN_CRIT KERN_SOH "" /* critical conditions */
#define KERN_ERR KERN_SOH "" /* error conditions */
#define KERN_WARNING KERN_SOH "" /* warning conditions */
#define KERN_NOTICE KERN_SOH "" /* normal but significant condition */
#define KERN_INFO KERN_SOH "" /* informational */
#define KERN_DEBUG KERN_SOH "" /* debug-level messages */
#define KERN_DEFAULT KERN_SOH "" /* the default kernel loglevel */
/*
* Annotation for a "continued" line of log printout (only done after a
* line that had no enclosing \n). Only to be used by core/arch code
* during early bootup (a continued line is not SMP-safe otherwise).
*/
#define KERN_CONT ""
#endif

View file

@ -0,0 +1,44 @@
#ifndef _LIBLOCKDEP_LINUX_KERNEL_H_
#define _LIBLOCKDEP_LINUX_KERNEL_H_
#include <linux/export.h>
#include <linux/types.h>
#include <linux/rcu.h>
#include <linux/hardirq.h>
#include <linux/kern_levels.h>
#ifndef container_of
#define container_of(ptr, type, member) ({ \
const typeof(((type *)0)->member) * __mptr = (ptr); \
(type *)((char *)__mptr - offsetof(type, member)); })
#endif
#define max(x, y) ({ \
typeof(x) _max1 = (x); \
typeof(y) _max2 = (y); \
(void) (&_max1 == &_max2); \
_max1 > _max2 ? _max1 : _max2; })
#define BUILD_BUG_ON(condition) ((void)sizeof(char[1 - 2*!!(condition)]))
#define WARN_ON(x) (x)
#define WARN_ON_ONCE(x) (x)
#define likely(x) (x)
#define WARN(x, y, z) (x)
#define uninitialized_var(x) x
#define __init
#define noinline
#define list_add_tail_rcu list_add_tail
#ifndef CALLER_ADDR0
#define CALLER_ADDR0 ((unsigned long)__builtin_return_address(0))
#endif
#ifndef _RET_IP_
#define _RET_IP_ CALLER_ADDR0
#endif
#ifndef _THIS_IP_
#define _THIS_IP_ ({ __label__ __here; __here: (unsigned long)&&__here; })
#endif
#endif

View file

@ -0,0 +1,8 @@
#ifndef _LIBLOCKDEP_LINUX_KMEMCHECK_H_
#define _LIBLOCKDEP_LINUX_KMEMCHECK_H_
static inline void kmemcheck_mark_initialized(void *address, unsigned int n)
{
}
#endif

View file

@ -0,0 +1,3 @@
/* empty file */

View file

@ -0,0 +1 @@
#include "../../../include/linux/list.h"

View file

@ -0,0 +1,58 @@
#ifndef _LIBLOCKDEP_LOCKDEP_H_
#define _LIBLOCKDEP_LOCKDEP_H_
#include <sys/prctl.h>
#include <sys/syscall.h>
#include <string.h>
#include <limits.h>
#include <linux/utsname.h>
#define MAX_LOCK_DEPTH 2000UL
#define asmlinkage
#define __visible
#include "../../../include/linux/lockdep.h"
struct task_struct {
u64 curr_chain_key;
int lockdep_depth;
unsigned int lockdep_recursion;
struct held_lock held_locks[MAX_LOCK_DEPTH];
gfp_t lockdep_reclaim_gfp;
int pid;
char comm[17];
};
extern struct task_struct *__curr(void);
#define current (__curr())
#define debug_locks_off() 1
#define task_pid_nr(tsk) ((tsk)->pid)
#define KSYM_NAME_LEN 128
#define printk printf
#define list_del_rcu list_del
#define atomic_t unsigned long
#define atomic_inc(x) ((*(x))++)
static struct new_utsname *init_utsname(void)
{
static struct new_utsname n = (struct new_utsname) {
.release = "liblockdep",
.version = LIBLOCKDEP_VERSION,
};
return &n;
}
#define print_tainted() ""
#define static_obj(x) 1
#define debug_show_all_locks()
#endif

View file

@ -0,0 +1,6 @@
#ifndef _LIBLOCKDEP_LINUX_MODULE_H_
#define _LIBLOCKDEP_LINUX_MODULE_H_
#define module_param(name, type, perm)
#endif

View file

@ -0,0 +1,3 @@
/* empty file */

View file

@ -0,0 +1 @@
#include "../../../include/linux/poison.h"

View file

@ -0,0 +1,6 @@
#ifndef _LIBLOCKDEP_LINUX_PREFETCH_H_
#define _LIBLOCKDEP_LINUX_PREFETCH_H
static inline void prefetch(void *a __attribute__((unused))) { }
#endif

View file

@ -0,0 +1,3 @@
/* empty file */

View file

@ -0,0 +1 @@
#include "../../../include/linux/rbtree.h"

View file

@ -0,0 +1,2 @@
#define __always_inline
#include "../../../include/linux/rbtree_augmented.h"

View file

@ -0,0 +1,21 @@
#ifndef _LIBLOCKDEP_RCU_H_
#define _LIBLOCKDEP_RCU_H_
int rcu_scheduler_active;
static inline int rcu_lockdep_current_cpu_online(void)
{
return 1;
}
static inline int rcu_is_cpu_idle(void)
{
return 1;
}
static inline bool rcu_is_watching(void)
{
return false;
}
#endif

View file

@ -0,0 +1,3 @@
/* empty file */

View file

@ -0,0 +1,25 @@
#ifndef _LIBLOCKDEP_SPINLOCK_H_
#define _LIBLOCKDEP_SPINLOCK_H_
#include <pthread.h>
#include <stdbool.h>
#define arch_spinlock_t pthread_mutex_t
#define __ARCH_SPIN_LOCK_UNLOCKED PTHREAD_MUTEX_INITIALIZER
static inline void arch_spin_lock(arch_spinlock_t *mutex)
{
pthread_mutex_lock(mutex);
}
static inline void arch_spin_unlock(arch_spinlock_t *mutex)
{
pthread_mutex_unlock(mutex);
}
static inline bool arch_spin_is_locked(arch_spinlock_t *mutex)
{
return true;
}
#endif

View file

@ -0,0 +1,32 @@
#ifndef _LIBLOCKDEP_LINUX_STACKTRACE_H_
#define _LIBLOCKDEP_LINUX_STACKTRACE_H_
#include <execinfo.h>
struct stack_trace {
unsigned int nr_entries, max_entries;
unsigned long *entries;
int skip;
};
static inline void print_stack_trace(struct stack_trace *trace, int spaces)
{
backtrace_symbols_fd((void **)trace->entries, trace->nr_entries, 1);
}
#define save_stack_trace(trace) \
((trace)->nr_entries = \
backtrace((void **)(trace)->entries, (trace)->max_entries))
static inline int dump_stack(void)
{
void *array[64];
size_t size;
size = backtrace(array, 64);
backtrace_symbols_fd(array, size, 1);
return 0;
}
#endif

View file

@ -0,0 +1,7 @@
#ifndef _LIBLOCKDEP_LINUX_STRINGIFY_H_
#define _LIBLOCKDEP_LINUX_STRINGIFY_H_
#define __stringify_1(x...) #x
#define __stringify(x...) __stringify_1(x)
#endif

View file

@ -0,0 +1,3 @@
/* empty file */

View file

@ -0,0 +1,58 @@
#include "symbol/kallsyms.h"
#include <stdio.h>
#include <stdlib.h>
int kallsyms__parse(const char *filename, void *arg,
int (*process_symbol)(void *arg, const char *name,
char type, u64 start))
{
char *line = NULL;
size_t n;
int err = -1;
FILE *file = fopen(filename, "r");
if (file == NULL)
goto out_failure;
err = 0;
while (!feof(file)) {
u64 start;
int line_len, len;
char symbol_type;
char *symbol_name;
line_len = getline(&line, &n, file);
if (line_len < 0 || !line)
break;
line[--line_len] = '\0'; /* \n */
len = hex2u64(line, &start);
len++;
if (len + 2 >= line_len)
continue;
symbol_type = line[len];
len += 2;
symbol_name = line + len;
len = line_len - len;
if (len >= KSYM_NAME_LEN) {
err = -1;
break;
}
err = process_symbol(arg, symbol_name, symbol_type, start);
if (err)
break;
}
free(line);
fclose(file);
return err;
out_failure:
return -1;
}

View file

@ -0,0 +1,24 @@
#ifndef __TOOLS_KALLSYMS_H_
#define __TOOLS_KALLSYMS_H_ 1
#include <elf.h>
#include <linux/ctype.h>
#include <linux/types.h>
#ifndef KSYM_NAME_LEN
#define KSYM_NAME_LEN 256
#endif
static inline u8 kallsyms2elf_type(char type)
{
if (type == 'W')
return STB_WEAK;
return isupper(type) ? STB_GLOBAL : STB_LOCAL;
}
int kallsyms__parse(const char *filename, void *arg,
int (*process_symbol)(void *arg, const char *name,
char type, u64 start));
#endif /* __TOOLS_KALLSYMS_H_ */

View file

@ -0,0 +1,340 @@
# trace-cmd version
EP_VERSION = 1
EP_PATCHLEVEL = 1
EP_EXTRAVERSION = 0
# file format version
FILE_VERSION = 6
MAKEFLAGS += --no-print-directory
# Makefiles suck: This macro sets a default value of $(2) for the
# variable named by $(1), unless the variable has been set by
# environment or command line. This is necessary for CC and AR
# because make sets default values, so the simpler ?= approach
# won't work as expected.
define allow-override
$(if $(or $(findstring environment,$(origin $(1))),\
$(findstring command line,$(origin $(1)))),,\
$(eval $(1) = $(2)))
endef
# Allow setting CC and AR, or setting CROSS_COMPILE as a prefix.
$(call allow-override,CC,$(CROSS_COMPILE)gcc)
$(call allow-override,AR,$(CROSS_COMPILE)ar)
EXT = -std=gnu99
INSTALL = install
# Use DESTDIR for installing into a different root directory.
# This is useful for building a package. The program will be
# installed in this directory as if it was the root directory.
# Then the build tool can move it later.
DESTDIR ?=
DESTDIR_SQ = '$(subst ','\'',$(DESTDIR))'
prefix ?= /usr/local
bindir_relative = bin
bindir = $(prefix)/$(bindir_relative)
man_dir = $(prefix)/share/man
man_dir_SQ = '$(subst ','\'',$(man_dir))'
export man_dir man_dir_SQ INSTALL
export DESTDIR DESTDIR_SQ
set_plugin_dir := 1
# Set plugin_dir to preffered global plugin location
# If we install under $HOME directory we go under
# $(HOME)/.traceevent/plugins
#
# We dont set PLUGIN_DIR in case we install under $HOME
# directory, because by default the code looks under:
# $(HOME)/.traceevent/plugins by default.
#
ifeq ($(plugin_dir),)
ifeq ($(prefix),$(HOME))
override plugin_dir = $(HOME)/.traceevent/plugins
set_plugin_dir := 0
else
override plugin_dir = $(prefix)/lib/traceevent/plugins
endif
endif
ifeq ($(set_plugin_dir),1)
PLUGIN_DIR = -DPLUGIN_DIR="$(plugin_dir)"
PLUGIN_DIR_SQ = '$(subst ','\'',$(PLUGIN_DIR))'
endif
include $(if $(BUILD_SRC),$(BUILD_SRC)/)../../scripts/Makefile.include
# copy a bit from Linux kbuild
ifeq ("$(origin V)", "command line")
VERBOSE = $(V)
endif
ifndef VERBOSE
VERBOSE = 0
endif
ifeq ("$(origin O)", "command line")
BUILD_OUTPUT := $(O)
endif
ifeq ($(BUILD_SRC),)
ifneq ($(OUTPUT),)
define build_output
$(if $(VERBOSE:1=),@)+$(MAKE) -C $(OUTPUT) \
BUILD_SRC=$(CURDIR)/ -f $(CURDIR)/Makefile $1
endef
all: sub-make
$(MAKECMDGOALS): sub-make
sub-make: force
$(call build_output, $(MAKECMDGOALS))
# Leave processing to above invocation of make
skip-makefile := 1
endif # OUTPUT
endif # BUILD_SRC
# We process the rest of the Makefile if this is the final invocation of make
ifeq ($(skip-makefile),)
srctree := $(if $(BUILD_SRC),$(BUILD_SRC),$(CURDIR))
objtree := $(CURDIR)
src := $(srctree)
obj := $(objtree)
export prefix bindir src obj
# Shell quotes
bindir_SQ = $(subst ','\'',$(bindir))
bindir_relative_SQ = $(subst ','\'',$(bindir_relative))
plugin_dir_SQ = $(subst ','\'',$(plugin_dir))
LIB_FILE = libtraceevent.a libtraceevent.so
CONFIG_INCLUDES =
CONFIG_LIBS =
CONFIG_FLAGS =
VERSION = $(EP_VERSION)
PATCHLEVEL = $(EP_PATCHLEVEL)
EXTRAVERSION = $(EP_EXTRAVERSION)
OBJ = $@
N =
export Q VERBOSE
EVENT_PARSE_VERSION = $(EP_VERSION).$(EP_PATCHLEVEL).$(EP_EXTRAVERSION)
INCLUDES = -I. -I $(srctree)/../../include $(CONFIG_INCLUDES)
# Set compile option CFLAGS if not set elsewhere
CFLAGS ?= -g -Wall
# Append required CFLAGS
override CFLAGS += $(CONFIG_FLAGS) $(INCLUDES) $(PLUGIN_DIR_SQ)
override CFLAGS += $(udis86-flags) -D_GNU_SOURCE
ifeq ($(VERBOSE),1)
Q =
else
Q = @
endif
do_compile_shared_library = \
($(print_shared_lib_compile) \
$(CC) --shared $^ -o $@)
do_plugin_build = \
($(print_plugin_build) \
$(CC) $(CFLAGS) -shared -nostartfiles -o $@ $<)
do_build_static_lib = \
($(print_static_lib_build) \
$(RM) $@; $(AR) rcs $@ $^)
do_compile = $(QUIET_CC)$(CC) -c $(CFLAGS) $(EXT) $< -o $(obj)/$@;
$(obj)/%.o: $(src)/%.c
$(call do_compile)
%.o: $(src)/%.c
$(call do_compile)
PEVENT_LIB_OBJS = event-parse.o
PEVENT_LIB_OBJS += event-plugin.o
PEVENT_LIB_OBJS += trace-seq.o
PEVENT_LIB_OBJS += parse-filter.o
PEVENT_LIB_OBJS += parse-utils.o
PEVENT_LIB_OBJS += kbuffer-parse.o
PLUGIN_OBJS = plugin_jbd2.o
PLUGIN_OBJS += plugin_hrtimer.o
PLUGIN_OBJS += plugin_kmem.o
PLUGIN_OBJS += plugin_kvm.o
PLUGIN_OBJS += plugin_mac80211.o
PLUGIN_OBJS += plugin_sched_switch.o
PLUGIN_OBJS += plugin_function.o
PLUGIN_OBJS += plugin_xen.o
PLUGIN_OBJS += plugin_scsi.o
PLUGIN_OBJS += plugin_cfg80211.o
PLUGINS := $(PLUGIN_OBJS:.o=.so)
ALL_OBJS = $(PEVENT_LIB_OBJS) $(PLUGIN_OBJS)
CMD_TARGETS = $(LIB_FILE) $(PLUGINS)
TARGETS = $(CMD_TARGETS)
all: all_cmd
all_cmd: $(CMD_TARGETS)
libtraceevent.so: $(PEVENT_LIB_OBJS)
$(QUIET_LINK)$(CC) --shared $^ -o $@
libtraceevent.a: $(PEVENT_LIB_OBJS)
$(QUIET_LINK)$(RM) $@; $(AR) rcs $@ $^
plugins: $(PLUGINS)
$(PEVENT_LIB_OBJS): %.o: $(src)/%.c TRACEEVENT-CFLAGS
$(QUIET_CC_FPIC)$(CC) -c $(CFLAGS) $(EXT) -fPIC $< -o $@
$(PLUGIN_OBJS): %.o : $(src)/%.c
$(QUIET_CC_FPIC)$(CC) -c $(CFLAGS) -fPIC -o $@ $<
$(PLUGINS): %.so: %.o
$(QUIET_LINK)$(CC) $(CFLAGS) -shared -nostartfiles -o $@ $<
define make_version.h
(echo '/* This file is automatically generated. Do not modify. */'; \
echo \#define VERSION_CODE $(shell \
expr $(VERSION) \* 256 + $(PATCHLEVEL)); \
echo '#define EXTRAVERSION ' $(EXTRAVERSION); \
echo '#define VERSION_STRING "'$(VERSION).$(PATCHLEVEL).$(EXTRAVERSION)'"'; \
echo '#define FILE_VERSION '$(FILE_VERSION); \
) > $1
endef
define update_version.h
($(call make_version.h, $@.tmp); \
if [ -r $@ ] && cmp -s $@ $@.tmp; then \
rm -f $@.tmp; \
else \
echo ' UPDATE $@'; \
mv -f $@.tmp $@; \
fi);
endef
ep_version.h: force
$(Q)$(N)$(call update_version.h)
VERSION_FILES = ep_version.h
define update_dir
(echo $1 > $@.tmp; \
if [ -r $@ ] && cmp -s $@ $@.tmp; then \
rm -f $@.tmp; \
else \
echo ' UPDATE $@'; \
mv -f $@.tmp $@; \
fi);
endef
## make deps
all_objs := $(sort $(ALL_OBJS))
all_deps := $(all_objs:%.o=.%.d)
# let .d file also depends on the source and header files
define check_deps
@set -e; $(RM) $@; \
$(CC) -MM $(CFLAGS) $< > $@.$$$$; \
sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \
$(RM) $@.$$$$
endef
$(all_deps): .%.d: $(src)/%.c
$(Q)$(call check_deps)
$(all_objs) : %.o : .%.d
dep_includes := $(wildcard $(all_deps))
ifneq ($(dep_includes),)
include $(dep_includes)
endif
### Detect environment changes
TRACK_CFLAGS = $(subst ','\'',$(CFLAGS)):$(ARCH):$(CROSS_COMPILE)
TRACEEVENT-CFLAGS: force
@FLAGS='$(TRACK_CFLAGS)'; \
if test x"$$FLAGS" != x"`cat TRACEEVENT-CFLAGS 2>/dev/null`" ; then \
echo 1>&2 " FLAGS: * new build flags or cross compiler"; \
echo "$$FLAGS" >TRACEEVENT-CFLAGS; \
fi
tags: force
$(RM) tags
find . -name '*.[ch]' | xargs ctags --extra=+f --c-kinds=+px \
--regex-c++='/_PE\(([^,)]*).*/PEVENT_ERRNO__\1/'
TAGS: force
$(RM) TAGS
find . -name '*.[ch]' | xargs etags \
--regex='/_PE(\([^,)]*\).*/PEVENT_ERRNO__\1/'
define do_install
if [ ! -d '$(DESTDIR_SQ)$2' ]; then \
$(INSTALL) -d -m 755 '$(DESTDIR_SQ)$2'; \
fi; \
$(INSTALL) $1 '$(DESTDIR_SQ)$2'
endef
define do_install_plugins
for plugin in $1; do \
$(call do_install,$$plugin,$(plugin_dir_SQ)); \
done
endef
install_lib: all_cmd install_plugins
$(call QUIET_INSTALL, $(LIB_FILE)) \
$(call do_install,$(LIB_FILE),$(bindir_SQ))
install_plugins: $(PLUGINS)
$(call QUIET_INSTALL, trace_plugins) \
$(call do_install_plugins, $(PLUGINS))
install: install_lib
clean:
$(call QUIET_CLEAN, libtraceevent) \
$(RM) *.o *~ $(TARGETS) *.a *.so $(VERSION_FILES) .*.d \
$(RM) TRACEEVENT-CFLAGS tags TAGS
endif # skip-makefile
PHONY += force plugins
force:
plugins:
@echo > /dev/null
# Declare the contents of the .PHONY variable as phony. We keep that
# information in a variable so we can use it in if_changed and friends.
.PHONY: $(PHONY)

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,944 @@
/*
* Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt <srostedt@redhat.com>
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License (not later!)
*
* This program 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, see <http://www.gnu.org/licenses>
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
#ifndef _PARSE_EVENTS_H
#define _PARSE_EVENTS_H
#include <stdbool.h>
#include <stdarg.h>
#include <regex.h>
#include <string.h>
#ifndef __maybe_unused
#define __maybe_unused __attribute__((unused))
#endif
/* ----------------------- trace_seq ----------------------- */
#ifndef TRACE_SEQ_BUF_SIZE
#define TRACE_SEQ_BUF_SIZE 4096
#endif
#ifndef DEBUG_RECORD
#define DEBUG_RECORD 0
#endif
struct pevent_record {
unsigned long long ts;
unsigned long long offset;
long long missed_events; /* buffer dropped events before */
int record_size; /* size of binary record */
int size; /* size of data */
void *data;
int cpu;
int ref_count;
int locked; /* Do not free, even if ref_count is zero */
void *priv;
#if DEBUG_RECORD
struct pevent_record *prev;
struct pevent_record *next;
long alloc_addr;
#endif
};
enum trace_seq_fail {
TRACE_SEQ__GOOD,
TRACE_SEQ__BUFFER_POISONED,
TRACE_SEQ__MEM_ALLOC_FAILED,
};
/*
* Trace sequences are used to allow a function to call several other functions
* to create a string of data to use (up to a max of PAGE_SIZE).
*/
struct trace_seq {
char *buffer;
unsigned int buffer_size;
unsigned int len;
unsigned int readpos;
enum trace_seq_fail state;
};
void trace_seq_init(struct trace_seq *s);
void trace_seq_reset(struct trace_seq *s);
void trace_seq_destroy(struct trace_seq *s);
extern int trace_seq_printf(struct trace_seq *s, const char *fmt, ...)
__attribute__ ((format (printf, 2, 3)));
extern int trace_seq_vprintf(struct trace_seq *s, const char *fmt, va_list args)
__attribute__ ((format (printf, 2, 0)));
extern int trace_seq_puts(struct trace_seq *s, const char *str);
extern int trace_seq_putc(struct trace_seq *s, unsigned char c);
extern void trace_seq_terminate(struct trace_seq *s);
extern int trace_seq_do_printf(struct trace_seq *s);
/* ----------------------- pevent ----------------------- */
struct pevent;
struct event_format;
typedef int (*pevent_event_handler_func)(struct trace_seq *s,
struct pevent_record *record,
struct event_format *event,
void *context);
typedef int (*pevent_plugin_load_func)(struct pevent *pevent);
typedef int (*pevent_plugin_unload_func)(struct pevent *pevent);
struct pevent_plugin_option {
struct pevent_plugin_option *next;
void *handle;
char *file;
char *name;
char *plugin_alias;
char *description;
char *value;
void *priv;
int set;
};
/*
* Plugin hooks that can be called:
*
* PEVENT_PLUGIN_LOADER: (required)
* The function name to initialized the plugin.
*
* int PEVENT_PLUGIN_LOADER(struct pevent *pevent)
*
* PEVENT_PLUGIN_UNLOADER: (optional)
* The function called just before unloading
*
* int PEVENT_PLUGIN_UNLOADER(struct pevent *pevent)
*
* PEVENT_PLUGIN_OPTIONS: (optional)
* Plugin options that can be set before loading
*
* struct pevent_plugin_option PEVENT_PLUGIN_OPTIONS[] = {
* {
* .name = "option-name",
* .plugin_alias = "overide-file-name", (optional)
* .description = "description of option to show users",
* },
* {
* .name = NULL,
* },
* };
*
* Array must end with .name = NULL;
*
*
* .plugin_alias is used to give a shorter name to access
* the vairable. Useful if a plugin handles more than one event.
*
* PEVENT_PLUGIN_ALIAS: (optional)
* The name to use for finding options (uses filename if not defined)
*/
#define PEVENT_PLUGIN_LOADER pevent_plugin_loader
#define PEVENT_PLUGIN_UNLOADER pevent_plugin_unloader
#define PEVENT_PLUGIN_OPTIONS pevent_plugin_options
#define PEVENT_PLUGIN_ALIAS pevent_plugin_alias
#define _MAKE_STR(x) #x
#define MAKE_STR(x) _MAKE_STR(x)
#define PEVENT_PLUGIN_LOADER_NAME MAKE_STR(PEVENT_PLUGIN_LOADER)
#define PEVENT_PLUGIN_UNLOADER_NAME MAKE_STR(PEVENT_PLUGIN_UNLOADER)
#define PEVENT_PLUGIN_OPTIONS_NAME MAKE_STR(PEVENT_PLUGIN_OPTIONS)
#define PEVENT_PLUGIN_ALIAS_NAME MAKE_STR(PEVENT_PLUGIN_ALIAS)
#define NSECS_PER_SEC 1000000000ULL
#define NSECS_PER_USEC 1000ULL
enum format_flags {
FIELD_IS_ARRAY = 1,
FIELD_IS_POINTER = 2,
FIELD_IS_SIGNED = 4,
FIELD_IS_STRING = 8,
FIELD_IS_DYNAMIC = 16,
FIELD_IS_LONG = 32,
FIELD_IS_FLAG = 64,
FIELD_IS_SYMBOLIC = 128,
};
struct format_field {
struct format_field *next;
struct event_format *event;
char *type;
char *name;
int offset;
int size;
unsigned int arraylen;
unsigned int elementsize;
unsigned long flags;
};
struct format {
int nr_common;
int nr_fields;
struct format_field *common_fields;
struct format_field *fields;
};
struct print_arg_atom {
char *atom;
};
struct print_arg_string {
char *string;
int offset;
};
struct print_arg_bitmask {
char *bitmask;
int offset;
};
struct print_arg_field {
char *name;
struct format_field *field;
};
struct print_flag_sym {
struct print_flag_sym *next;
char *value;
char *str;
};
struct print_arg_typecast {
char *type;
struct print_arg *item;
};
struct print_arg_flags {
struct print_arg *field;
char *delim;
struct print_flag_sym *flags;
};
struct print_arg_symbol {
struct print_arg *field;
struct print_flag_sym *symbols;
};
struct print_arg_hex {
struct print_arg *field;
struct print_arg *size;
};
struct print_arg_dynarray {
struct format_field *field;
struct print_arg *index;
};
struct print_arg;
struct print_arg_op {
char *op;
int prio;
struct print_arg *left;
struct print_arg *right;
};
struct pevent_function_handler;
struct print_arg_func {
struct pevent_function_handler *func;
struct print_arg *args;
};
enum print_arg_type {
PRINT_NULL,
PRINT_ATOM,
PRINT_FIELD,
PRINT_FLAGS,
PRINT_SYMBOL,
PRINT_HEX,
PRINT_TYPE,
PRINT_STRING,
PRINT_BSTRING,
PRINT_DYNAMIC_ARRAY,
PRINT_OP,
PRINT_FUNC,
PRINT_BITMASK,
};
struct print_arg {
struct print_arg *next;
enum print_arg_type type;
union {
struct print_arg_atom atom;
struct print_arg_field field;
struct print_arg_typecast typecast;
struct print_arg_flags flags;
struct print_arg_symbol symbol;
struct print_arg_hex hex;
struct print_arg_func func;
struct print_arg_string string;
struct print_arg_bitmask bitmask;
struct print_arg_op op;
struct print_arg_dynarray dynarray;
};
};
struct print_fmt {
char *format;
struct print_arg *args;
};
struct event_format {
struct pevent *pevent;
char *name;
int id;
int flags;
struct format format;
struct print_fmt print_fmt;
char *system;
pevent_event_handler_func handler;
void *context;
};
enum {
EVENT_FL_ISFTRACE = 0x01,
EVENT_FL_ISPRINT = 0x02,
EVENT_FL_ISBPRINT = 0x04,
EVENT_FL_ISFUNCENT = 0x10,
EVENT_FL_ISFUNCRET = 0x20,
EVENT_FL_NOHANDLE = 0x40,
EVENT_FL_PRINTRAW = 0x80,
EVENT_FL_FAILED = 0x80000000
};
enum event_sort_type {
EVENT_SORT_ID,
EVENT_SORT_NAME,
EVENT_SORT_SYSTEM,
};
enum event_type {
EVENT_ERROR,
EVENT_NONE,
EVENT_SPACE,
EVENT_NEWLINE,
EVENT_OP,
EVENT_DELIM,
EVENT_ITEM,
EVENT_DQUOTE,
EVENT_SQUOTE,
};
typedef unsigned long long (*pevent_func_handler)(struct trace_seq *s,
unsigned long long *args);
enum pevent_func_arg_type {
PEVENT_FUNC_ARG_VOID,
PEVENT_FUNC_ARG_INT,
PEVENT_FUNC_ARG_LONG,
PEVENT_FUNC_ARG_STRING,
PEVENT_FUNC_ARG_PTR,
PEVENT_FUNC_ARG_MAX_TYPES
};
enum pevent_flag {
PEVENT_NSEC_OUTPUT = 1, /* output in NSECS */
PEVENT_DISABLE_SYS_PLUGINS = 1 << 1,
PEVENT_DISABLE_PLUGINS = 1 << 2,
};
#define PEVENT_ERRORS \
_PE(MEM_ALLOC_FAILED, "failed to allocate memory"), \
_PE(PARSE_EVENT_FAILED, "failed to parse event"), \
_PE(READ_ID_FAILED, "failed to read event id"), \
_PE(READ_FORMAT_FAILED, "failed to read event format"), \
_PE(READ_PRINT_FAILED, "failed to read event print fmt"), \
_PE(OLD_FTRACE_ARG_FAILED,"failed to allocate field name for ftrace"),\
_PE(INVALID_ARG_TYPE, "invalid argument type"), \
_PE(INVALID_EXP_TYPE, "invalid expression type"), \
_PE(INVALID_OP_TYPE, "invalid operator type"), \
_PE(INVALID_EVENT_NAME, "invalid event name"), \
_PE(EVENT_NOT_FOUND, "no event found"), \
_PE(SYNTAX_ERROR, "syntax error"), \
_PE(ILLEGAL_RVALUE, "illegal rvalue"), \
_PE(ILLEGAL_LVALUE, "illegal lvalue for string comparison"), \
_PE(INVALID_REGEX, "regex did not compute"), \
_PE(ILLEGAL_STRING_CMP, "illegal comparison for string"), \
_PE(ILLEGAL_INTEGER_CMP,"illegal comparison for integer"), \
_PE(REPARENT_NOT_OP, "cannot reparent other than OP"), \
_PE(REPARENT_FAILED, "failed to reparent filter OP"), \
_PE(BAD_FILTER_ARG, "bad arg in filter tree"), \
_PE(UNEXPECTED_TYPE, "unexpected type (not a value)"), \
_PE(ILLEGAL_TOKEN, "illegal token"), \
_PE(INVALID_PAREN, "open parenthesis cannot come here"), \
_PE(UNBALANCED_PAREN, "unbalanced number of parenthesis"), \
_PE(UNKNOWN_TOKEN, "unknown token"), \
_PE(FILTER_NOT_FOUND, "no filter found"), \
_PE(NOT_A_NUMBER, "must have number field"), \
_PE(NO_FILTER, "no filters exists"), \
_PE(FILTER_MISS, "record does not match to filter")
#undef _PE
#define _PE(__code, __str) PEVENT_ERRNO__ ## __code
enum pevent_errno {
PEVENT_ERRNO__SUCCESS = 0,
PEVENT_ERRNO__FILTER_MATCH = PEVENT_ERRNO__SUCCESS,
/*
* Choose an arbitrary negative big number not to clash with standard
* errno since SUS requires the errno has distinct positive values.
* See 'Issue 6' in the link below.
*
* http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/errno.h.html
*/
__PEVENT_ERRNO__START = -100000,
PEVENT_ERRORS,
__PEVENT_ERRNO__END,
};
#undef _PE
struct plugin_list;
#define INVALID_PLUGIN_LIST_OPTION ((char **)((unsigned long)-1))
struct plugin_list *traceevent_load_plugins(struct pevent *pevent);
void traceevent_unload_plugins(struct plugin_list *plugin_list,
struct pevent *pevent);
char **traceevent_plugin_list_options(void);
void traceevent_plugin_free_options_list(char **list);
int traceevent_plugin_add_options(const char *name,
struct pevent_plugin_option *options);
void traceevent_plugin_remove_options(struct pevent_plugin_option *options);
void traceevent_print_plugins(struct trace_seq *s,
const char *prefix, const char *suffix,
const struct plugin_list *list);
struct cmdline;
struct cmdline_list;
struct func_map;
struct func_list;
struct event_handler;
struct pevent {
int ref_count;
int header_page_ts_offset;
int header_page_ts_size;
int header_page_size_offset;
int header_page_size_size;
int header_page_data_offset;
int header_page_data_size;
int header_page_overwrite;
int file_bigendian;
int host_bigendian;
int latency_format;
int old_format;
int cpus;
int long_size;
int page_size;
struct cmdline *cmdlines;
struct cmdline_list *cmdlist;
int cmdline_count;
struct func_map *func_map;
struct func_list *funclist;
unsigned int func_count;
struct printk_map *printk_map;
struct printk_list *printklist;
unsigned int printk_count;
struct event_format **events;
int nr_events;
struct event_format **sort_events;
enum event_sort_type last_type;
int type_offset;
int type_size;
int pid_offset;
int pid_size;
int pc_offset;
int pc_size;
int flags_offset;
int flags_size;
int ld_offset;
int ld_size;
int print_raw;
int test_filters;
int flags;
struct format_field *bprint_ip_field;
struct format_field *bprint_fmt_field;
struct format_field *bprint_buf_field;
struct event_handler *handlers;
struct pevent_function_handler *func_handlers;
/* cache */
struct event_format *last_event;
char *trace_clock;
};
static inline void pevent_set_flag(struct pevent *pevent, int flag)
{
pevent->flags |= flag;
}
static inline unsigned short
__data2host2(struct pevent *pevent, unsigned short data)
{
unsigned short swap;
if (pevent->host_bigendian == pevent->file_bigendian)
return data;
swap = ((data & 0xffULL) << 8) |
((data & (0xffULL << 8)) >> 8);
return swap;
}
static inline unsigned int
__data2host4(struct pevent *pevent, unsigned int data)
{
unsigned int swap;
if (pevent->host_bigendian == pevent->file_bigendian)
return data;
swap = ((data & 0xffULL) << 24) |
((data & (0xffULL << 8)) << 8) |
((data & (0xffULL << 16)) >> 8) |
((data & (0xffULL << 24)) >> 24);
return swap;
}
static inline unsigned long long
__data2host8(struct pevent *pevent, unsigned long long data)
{
unsigned long long swap;
if (pevent->host_bigendian == pevent->file_bigendian)
return data;
swap = ((data & 0xffULL) << 56) |
((data & (0xffULL << 8)) << 40) |
((data & (0xffULL << 16)) << 24) |
((data & (0xffULL << 24)) << 8) |
((data & (0xffULL << 32)) >> 8) |
((data & (0xffULL << 40)) >> 24) |
((data & (0xffULL << 48)) >> 40) |
((data & (0xffULL << 56)) >> 56);
return swap;
}
#define data2host2(pevent, ptr) __data2host2(pevent, *(unsigned short *)(ptr))
#define data2host4(pevent, ptr) __data2host4(pevent, *(unsigned int *)(ptr))
#define data2host8(pevent, ptr) \
({ \
unsigned long long __val; \
\
memcpy(&__val, (ptr), sizeof(unsigned long long)); \
__data2host8(pevent, __val); \
})
static inline int traceevent_host_bigendian(void)
{
unsigned char str[] = { 0x1, 0x2, 0x3, 0x4 };
unsigned int val;
memcpy(&val, str, 4);
return val == 0x01020304;
}
/* taken from kernel/trace/trace.h */
enum trace_flag_type {
TRACE_FLAG_IRQS_OFF = 0x01,
TRACE_FLAG_IRQS_NOSUPPORT = 0x02,
TRACE_FLAG_NEED_RESCHED = 0x04,
TRACE_FLAG_HARDIRQ = 0x08,
TRACE_FLAG_SOFTIRQ = 0x10,
};
int pevent_register_comm(struct pevent *pevent, const char *comm, int pid);
void pevent_register_trace_clock(struct pevent *pevent, char *trace_clock);
int pevent_register_function(struct pevent *pevent, char *name,
unsigned long long addr, char *mod);
int pevent_register_print_string(struct pevent *pevent, const char *fmt,
unsigned long long addr);
int pevent_pid_is_registered(struct pevent *pevent, int pid);
void pevent_print_event(struct pevent *pevent, struct trace_seq *s,
struct pevent_record *record, bool use_trace_clock);
int pevent_parse_header_page(struct pevent *pevent, char *buf, unsigned long size,
int long_size);
enum pevent_errno pevent_parse_event(struct pevent *pevent, const char *buf,
unsigned long size, const char *sys);
enum pevent_errno pevent_parse_format(struct pevent *pevent,
struct event_format **eventp,
const char *buf,
unsigned long size, const char *sys);
void pevent_free_format(struct event_format *event);
void *pevent_get_field_raw(struct trace_seq *s, struct event_format *event,
const char *name, struct pevent_record *record,
int *len, int err);
int pevent_get_field_val(struct trace_seq *s, struct event_format *event,
const char *name, struct pevent_record *record,
unsigned long long *val, int err);
int pevent_get_common_field_val(struct trace_seq *s, struct event_format *event,
const char *name, struct pevent_record *record,
unsigned long long *val, int err);
int pevent_get_any_field_val(struct trace_seq *s, struct event_format *event,
const char *name, struct pevent_record *record,
unsigned long long *val, int err);
int pevent_print_num_field(struct trace_seq *s, const char *fmt,
struct event_format *event, const char *name,
struct pevent_record *record, int err);
int pevent_print_func_field(struct trace_seq *s, const char *fmt,
struct event_format *event, const char *name,
struct pevent_record *record, int err);
int pevent_register_event_handler(struct pevent *pevent, int id,
const char *sys_name, const char *event_name,
pevent_event_handler_func func, void *context);
int pevent_unregister_event_handler(struct pevent *pevent, int id,
const char *sys_name, const char *event_name,
pevent_event_handler_func func, void *context);
int pevent_register_print_function(struct pevent *pevent,
pevent_func_handler func,
enum pevent_func_arg_type ret_type,
char *name, ...);
int pevent_unregister_print_function(struct pevent *pevent,
pevent_func_handler func, char *name);
struct format_field *pevent_find_common_field(struct event_format *event, const char *name);
struct format_field *pevent_find_field(struct event_format *event, const char *name);
struct format_field *pevent_find_any_field(struct event_format *event, const char *name);
const char *pevent_find_function(struct pevent *pevent, unsigned long long addr);
unsigned long long
pevent_find_function_address(struct pevent *pevent, unsigned long long addr);
unsigned long long pevent_read_number(struct pevent *pevent, const void *ptr, int size);
int pevent_read_number_field(struct format_field *field, const void *data,
unsigned long long *value);
struct event_format *pevent_find_event(struct pevent *pevent, int id);
struct event_format *
pevent_find_event_by_name(struct pevent *pevent, const char *sys, const char *name);
void pevent_data_lat_fmt(struct pevent *pevent,
struct trace_seq *s, struct pevent_record *record);
int pevent_data_type(struct pevent *pevent, struct pevent_record *rec);
struct event_format *pevent_data_event_from_type(struct pevent *pevent, int type);
int pevent_data_pid(struct pevent *pevent, struct pevent_record *rec);
const char *pevent_data_comm_from_pid(struct pevent *pevent, int pid);
void pevent_event_info(struct trace_seq *s, struct event_format *event,
struct pevent_record *record);
int pevent_strerror(struct pevent *pevent, enum pevent_errno errnum,
char *buf, size_t buflen);
struct event_format **pevent_list_events(struct pevent *pevent, enum event_sort_type);
struct format_field **pevent_event_common_fields(struct event_format *event);
struct format_field **pevent_event_fields(struct event_format *event);
static inline int pevent_get_cpus(struct pevent *pevent)
{
return pevent->cpus;
}
static inline void pevent_set_cpus(struct pevent *pevent, int cpus)
{
pevent->cpus = cpus;
}
static inline int pevent_get_long_size(struct pevent *pevent)
{
return pevent->long_size;
}
static inline void pevent_set_long_size(struct pevent *pevent, int long_size)
{
pevent->long_size = long_size;
}
static inline int pevent_get_page_size(struct pevent *pevent)
{
return pevent->page_size;
}
static inline void pevent_set_page_size(struct pevent *pevent, int _page_size)
{
pevent->page_size = _page_size;
}
static inline int pevent_is_file_bigendian(struct pevent *pevent)
{
return pevent->file_bigendian;
}
static inline void pevent_set_file_bigendian(struct pevent *pevent, int endian)
{
pevent->file_bigendian = endian;
}
static inline int pevent_is_host_bigendian(struct pevent *pevent)
{
return pevent->host_bigendian;
}
static inline void pevent_set_host_bigendian(struct pevent *pevent, int endian)
{
pevent->host_bigendian = endian;
}
static inline int pevent_is_latency_format(struct pevent *pevent)
{
return pevent->latency_format;
}
static inline void pevent_set_latency_format(struct pevent *pevent, int lat)
{
pevent->latency_format = lat;
}
struct pevent *pevent_alloc(void);
void pevent_free(struct pevent *pevent);
void pevent_ref(struct pevent *pevent);
void pevent_unref(struct pevent *pevent);
/* access to the internal parser */
void pevent_buffer_init(const char *buf, unsigned long long size);
enum event_type pevent_read_token(char **tok);
void pevent_free_token(char *token);
int pevent_peek_char(void);
const char *pevent_get_input_buf(void);
unsigned long long pevent_get_input_buf_ptr(void);
/* for debugging */
void pevent_print_funcs(struct pevent *pevent);
void pevent_print_printk(struct pevent *pevent);
/* ----------------------- filtering ----------------------- */
enum filter_boolean_type {
FILTER_FALSE,
FILTER_TRUE,
};
enum filter_op_type {
FILTER_OP_AND = 1,
FILTER_OP_OR,
FILTER_OP_NOT,
};
enum filter_cmp_type {
FILTER_CMP_NONE,
FILTER_CMP_EQ,
FILTER_CMP_NE,
FILTER_CMP_GT,
FILTER_CMP_LT,
FILTER_CMP_GE,
FILTER_CMP_LE,
FILTER_CMP_MATCH,
FILTER_CMP_NOT_MATCH,
FILTER_CMP_REGEX,
FILTER_CMP_NOT_REGEX,
};
enum filter_exp_type {
FILTER_EXP_NONE,
FILTER_EXP_ADD,
FILTER_EXP_SUB,
FILTER_EXP_MUL,
FILTER_EXP_DIV,
FILTER_EXP_MOD,
FILTER_EXP_RSHIFT,
FILTER_EXP_LSHIFT,
FILTER_EXP_AND,
FILTER_EXP_OR,
FILTER_EXP_XOR,
FILTER_EXP_NOT,
};
enum filter_arg_type {
FILTER_ARG_NONE,
FILTER_ARG_BOOLEAN,
FILTER_ARG_VALUE,
FILTER_ARG_FIELD,
FILTER_ARG_EXP,
FILTER_ARG_OP,
FILTER_ARG_NUM,
FILTER_ARG_STR,
};
enum filter_value_type {
FILTER_NUMBER,
FILTER_STRING,
FILTER_CHAR
};
struct fliter_arg;
struct filter_arg_boolean {
enum filter_boolean_type value;
};
struct filter_arg_field {
struct format_field *field;
};
struct filter_arg_value {
enum filter_value_type type;
union {
char *str;
unsigned long long val;
};
};
struct filter_arg_op {
enum filter_op_type type;
struct filter_arg *left;
struct filter_arg *right;
};
struct filter_arg_exp {
enum filter_exp_type type;
struct filter_arg *left;
struct filter_arg *right;
};
struct filter_arg_num {
enum filter_cmp_type type;
struct filter_arg *left;
struct filter_arg *right;
};
struct filter_arg_str {
enum filter_cmp_type type;
struct format_field *field;
char *val;
char *buffer;
regex_t reg;
};
struct filter_arg {
enum filter_arg_type type;
union {
struct filter_arg_boolean boolean;
struct filter_arg_field field;
struct filter_arg_value value;
struct filter_arg_op op;
struct filter_arg_exp exp;
struct filter_arg_num num;
struct filter_arg_str str;
};
};
struct filter_type {
int event_id;
struct event_format *event;
struct filter_arg *filter;
};
#define PEVENT_FILTER_ERROR_BUFSZ 1024
struct event_filter {
struct pevent *pevent;
int filters;
struct filter_type *event_filters;
char error_buffer[PEVENT_FILTER_ERROR_BUFSZ];
};
struct event_filter *pevent_filter_alloc(struct pevent *pevent);
/* for backward compatibility */
#define FILTER_NONE PEVENT_ERRNO__NO_FILTER
#define FILTER_NOEXIST PEVENT_ERRNO__FILTER_NOT_FOUND
#define FILTER_MISS PEVENT_ERRNO__FILTER_MISS
#define FILTER_MATCH PEVENT_ERRNO__FILTER_MATCH
enum filter_trivial_type {
FILTER_TRIVIAL_FALSE,
FILTER_TRIVIAL_TRUE,
FILTER_TRIVIAL_BOTH,
};
enum pevent_errno pevent_filter_add_filter_str(struct event_filter *filter,
const char *filter_str);
enum pevent_errno pevent_filter_match(struct event_filter *filter,
struct pevent_record *record);
int pevent_filter_strerror(struct event_filter *filter, enum pevent_errno err,
char *buf, size_t buflen);
int pevent_event_filtered(struct event_filter *filter,
int event_id);
void pevent_filter_reset(struct event_filter *filter);
int pevent_filter_clear_trivial(struct event_filter *filter,
enum filter_trivial_type type);
void pevent_filter_free(struct event_filter *filter);
char *pevent_filter_make_string(struct event_filter *filter, int event_id);
int pevent_filter_remove_event(struct event_filter *filter,
int event_id);
int pevent_filter_event_has_trivial(struct event_filter *filter,
int event_id,
enum filter_trivial_type type);
int pevent_filter_copy(struct event_filter *dest, struct event_filter *source);
int pevent_update_trivial(struct event_filter *dest, struct event_filter *source,
enum filter_trivial_type type);
int pevent_filter_compare(struct event_filter *filter1, struct event_filter *filter2);
#endif /* _PARSE_EVENTS_H */

View file

@ -0,0 +1,416 @@
/*
* Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt <srostedt@redhat.com>
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License (not later!)
*
* This program 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, see <http://www.gnu.org/licenses>
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
#include <stdio.h>
#include <string.h>
#include <dlfcn.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <dirent.h>
#include "event-parse.h"
#include "event-utils.h"
#define LOCAL_PLUGIN_DIR ".traceevent/plugins"
static struct registered_plugin_options {
struct registered_plugin_options *next;
struct pevent_plugin_option *options;
} *registered_options;
static struct trace_plugin_options {
struct trace_plugin_options *next;
char *plugin;
char *option;
char *value;
} *trace_plugin_options;
struct plugin_list {
struct plugin_list *next;
char *name;
void *handle;
};
/**
* traceevent_plugin_list_options - get list of plugin options
*
* Returns an array of char strings that list the currently registered
* plugin options in the format of <plugin>:<option>. This list can be
* used by toggling the option.
*
* Returns NULL if there's no options registered. On error it returns
* INVALID_PLUGIN_LIST_OPTION
*
* Must be freed with traceevent_plugin_free_options_list().
*/
char **traceevent_plugin_list_options(void)
{
struct registered_plugin_options *reg;
struct pevent_plugin_option *op;
char **list = NULL;
char *name;
int count = 0;
for (reg = registered_options; reg; reg = reg->next) {
for (op = reg->options; op->name; op++) {
char *alias = op->plugin_alias ? op->plugin_alias : op->file;
char **temp = list;
name = malloc(strlen(op->name) + strlen(alias) + 2);
if (!name)
goto err;
sprintf(name, "%s:%s", alias, op->name);
list = realloc(list, count + 2);
if (!list) {
list = temp;
free(name);
goto err;
}
list[count++] = name;
list[count] = NULL;
}
}
return list;
err:
while (--count >= 0)
free(list[count]);
free(list);
return INVALID_PLUGIN_LIST_OPTION;
}
void traceevent_plugin_free_options_list(char **list)
{
int i;
if (!list)
return;
if (list == INVALID_PLUGIN_LIST_OPTION)
return;
for (i = 0; list[i]; i++)
free(list[i]);
free(list);
}
static int
update_option(const char *file, struct pevent_plugin_option *option)
{
struct trace_plugin_options *op;
char *plugin;
if (option->plugin_alias) {
plugin = strdup(option->plugin_alias);
if (!plugin)
return -1;
} else {
char *p;
plugin = strdup(file);
if (!plugin)
return -1;
p = strstr(plugin, ".");
if (p)
*p = '\0';
}
/* first look for named options */
for (op = trace_plugin_options; op; op = op->next) {
if (!op->plugin)
continue;
if (strcmp(op->plugin, plugin) != 0)
continue;
if (strcmp(op->option, option->name) != 0)
continue;
option->value = op->value;
option->set ^= 1;
goto out;
}
/* first look for unnamed options */
for (op = trace_plugin_options; op; op = op->next) {
if (op->plugin)
continue;
if (strcmp(op->option, option->name) != 0)
continue;
option->value = op->value;
option->set ^= 1;
break;
}
out:
free(plugin);
return 0;
}
/**
* traceevent_plugin_add_options - Add a set of options by a plugin
* @name: The name of the plugin adding the options
* @options: The set of options being loaded
*
* Sets the options with the values that have been added by user.
*/
int traceevent_plugin_add_options(const char *name,
struct pevent_plugin_option *options)
{
struct registered_plugin_options *reg;
reg = malloc(sizeof(*reg));
if (!reg)
return -1;
reg->next = registered_options;
reg->options = options;
registered_options = reg;
while (options->name) {
update_option(name, options);
options++;
}
return 0;
}
/**
* traceevent_plugin_remove_options - remove plugin options that were registered
* @options: Options to removed that were registered with traceevent_plugin_add_options
*/
void traceevent_plugin_remove_options(struct pevent_plugin_option *options)
{
struct registered_plugin_options **last;
struct registered_plugin_options *reg;
for (last = &registered_options; *last; last = &(*last)->next) {
if ((*last)->options == options) {
reg = *last;
*last = reg->next;
free(reg);
return;
}
}
}
/**
* traceevent_print_plugins - print out the list of plugins loaded
* @s: the trace_seq descripter to write to
* @prefix: The prefix string to add before listing the option name
* @suffix: The suffix string ot append after the option name
* @list: The list of plugins (usually returned by traceevent_load_plugins()
*
* Writes to the trace_seq @s the list of plugins (files) that is
* returned by traceevent_load_plugins(). Use @prefix and @suffix for formating:
* @prefix = " ", @suffix = "\n".
*/
void traceevent_print_plugins(struct trace_seq *s,
const char *prefix, const char *suffix,
const struct plugin_list *list)
{
while (list) {
trace_seq_printf(s, "%s%s%s", prefix, list->name, suffix);
list = list->next;
}
}
static void
load_plugin(struct pevent *pevent, const char *path,
const char *file, void *data)
{
struct plugin_list **plugin_list = data;
pevent_plugin_load_func func;
struct plugin_list *list;
const char *alias;
char *plugin;
void *handle;
plugin = malloc(strlen(path) + strlen(file) + 2);
if (!plugin) {
warning("could not allocate plugin memory\n");
return;
}
strcpy(plugin, path);
strcat(plugin, "/");
strcat(plugin, file);
handle = dlopen(plugin, RTLD_NOW | RTLD_GLOBAL);
if (!handle) {
warning("could not load plugin '%s'\n%s\n",
plugin, dlerror());
goto out_free;
}
alias = dlsym(handle, PEVENT_PLUGIN_ALIAS_NAME);
if (!alias)
alias = file;
func = dlsym(handle, PEVENT_PLUGIN_LOADER_NAME);
if (!func) {
warning("could not find func '%s' in plugin '%s'\n%s\n",
PEVENT_PLUGIN_LOADER_NAME, plugin, dlerror());
goto out_free;
}
list = malloc(sizeof(*list));
if (!list) {
warning("could not allocate plugin memory\n");
goto out_free;
}
list->next = *plugin_list;
list->handle = handle;
list->name = plugin;
*plugin_list = list;
pr_stat("registering plugin: %s", plugin);
func(pevent);
return;
out_free:
free(plugin);
}
static void
load_plugins_dir(struct pevent *pevent, const char *suffix,
const char *path,
void (*load_plugin)(struct pevent *pevent,
const char *path,
const char *name,
void *data),
void *data)
{
struct dirent *dent;
struct stat st;
DIR *dir;
int ret;
ret = stat(path, &st);
if (ret < 0)
return;
if (!S_ISDIR(st.st_mode))
return;
dir = opendir(path);
if (!dir)
return;
while ((dent = readdir(dir))) {
const char *name = dent->d_name;
if (strcmp(name, ".") == 0 ||
strcmp(name, "..") == 0)
continue;
/* Only load plugins that end in suffix */
if (strcmp(name + (strlen(name) - strlen(suffix)), suffix) != 0)
continue;
load_plugin(pevent, path, name, data);
}
closedir(dir);
}
static void
load_plugins(struct pevent *pevent, const char *suffix,
void (*load_plugin)(struct pevent *pevent,
const char *path,
const char *name,
void *data),
void *data)
{
char *home;
char *path;
char *envdir;
if (pevent->flags & PEVENT_DISABLE_PLUGINS)
return;
/*
* If a system plugin directory was defined,
* check that first.
*/
#ifdef PLUGIN_DIR
if (!(pevent->flags & PEVENT_DISABLE_SYS_PLUGINS))
load_plugins_dir(pevent, suffix, PLUGIN_DIR,
load_plugin, data);
#endif
/*
* Next let the environment-set plugin directory
* override the system defaults.
*/
envdir = getenv("TRACEEVENT_PLUGIN_DIR");
if (envdir)
load_plugins_dir(pevent, suffix, envdir, load_plugin, data);
/*
* Now let the home directory override the environment
* or system defaults.
*/
home = getenv("HOME");
if (!home)
return;
path = malloc(strlen(home) + strlen(LOCAL_PLUGIN_DIR) + 2);
if (!path) {
warning("could not allocate plugin memory\n");
return;
}
strcpy(path, home);
strcat(path, "/");
strcat(path, LOCAL_PLUGIN_DIR);
load_plugins_dir(pevent, suffix, path, load_plugin, data);
free(path);
}
struct plugin_list*
traceevent_load_plugins(struct pevent *pevent)
{
struct plugin_list *list = NULL;
load_plugins(pevent, ".so", load_plugin, &list);
return list;
}
void
traceevent_unload_plugins(struct plugin_list *plugin_list, struct pevent *pevent)
{
pevent_plugin_unload_func func;
struct plugin_list *list;
while (plugin_list) {
list = plugin_list;
plugin_list = list->next;
func = dlsym(list->handle, PEVENT_PLUGIN_UNLOADER_NAME);
if (func)
func(pevent);
dlclose(list->handle);
free(list->name);
free(list);
}
}

View file

@ -0,0 +1,81 @@
/*
* Copyright (C) 2010 Red Hat Inc, Steven Rostedt <srostedt@redhat.com>
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License (not later!)
*
* This program 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, see <http://www.gnu.org/licenses>
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
#ifndef __UTIL_H
#define __UTIL_H
#include <ctype.h>
/* Can be overridden */
void warning(const char *fmt, ...);
void pr_stat(const char *fmt, ...);
void vpr_stat(const char *fmt, va_list ap);
/* Always available */
void __warning(const char *fmt, ...);
void __pr_stat(const char *fmt, ...);
void __vwarning(const char *fmt, ...);
void __vpr_stat(const char *fmt, ...);
#define min(x, y) ({ \
typeof(x) _min1 = (x); \
typeof(y) _min2 = (y); \
(void) (&_min1 == &_min2); \
_min1 < _min2 ? _min1 : _min2; })
static inline char *strim(char *string)
{
char *ret;
if (!string)
return NULL;
while (*string) {
if (!isspace(*string))
break;
string++;
}
ret = string;
string = ret + strlen(ret) - 1;
while (string > ret) {
if (!isspace(*string))
break;
string--;
}
string[1] = 0;
return ret;
}
static inline int has_text(const char *text)
{
if (!text)
return 0;
while (*text) {
if (!isspace(*text))
return 1;
text++;
}
return 0;
}
#endif

View file

@ -0,0 +1,731 @@
/*
* Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt <srostedt@redhat.com>
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License (not later!)
*
* This program 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "kbuffer.h"
#define MISSING_EVENTS (1 << 31)
#define MISSING_STORED (1 << 30)
#define COMMIT_MASK ((1 << 27) - 1)
enum {
KBUFFER_FL_HOST_BIG_ENDIAN = (1<<0),
KBUFFER_FL_BIG_ENDIAN = (1<<1),
KBUFFER_FL_LONG_8 = (1<<2),
KBUFFER_FL_OLD_FORMAT = (1<<3),
};
#define ENDIAN_MASK (KBUFFER_FL_HOST_BIG_ENDIAN | KBUFFER_FL_BIG_ENDIAN)
/** kbuffer
* @timestamp - timestamp of current event
* @lost_events - # of lost events between this subbuffer and previous
* @flags - special flags of the kbuffer
* @subbuffer - pointer to the sub-buffer page
* @data - pointer to the start of data on the sub-buffer page
* @index - index from @data to the @curr event data
* @curr - offset from @data to the start of current event
* (includes metadata)
* @next - offset from @data to the start of next event
* @size - The size of data on @data
* @start - The offset from @subbuffer where @data lives
*
* @read_4 - Function to read 4 raw bytes (may swap)
* @read_8 - Function to read 8 raw bytes (may swap)
* @read_long - Function to read a long word (4 or 8 bytes with needed swap)
*/
struct kbuffer {
unsigned long long timestamp;
long long lost_events;
unsigned long flags;
void *subbuffer;
void *data;
unsigned int index;
unsigned int curr;
unsigned int next;
unsigned int size;
unsigned int start;
unsigned int (*read_4)(void *ptr);
unsigned long long (*read_8)(void *ptr);
unsigned long long (*read_long)(struct kbuffer *kbuf, void *ptr);
int (*next_event)(struct kbuffer *kbuf);
};
static void *zmalloc(size_t size)
{
return calloc(1, size);
}
static int host_is_bigendian(void)
{
unsigned char str[] = { 0x1, 0x2, 0x3, 0x4 };
unsigned int *ptr;
ptr = (unsigned int *)str;
return *ptr == 0x01020304;
}
static int do_swap(struct kbuffer *kbuf)
{
return ((kbuf->flags & KBUFFER_FL_HOST_BIG_ENDIAN) + kbuf->flags) &
ENDIAN_MASK;
}
static unsigned long long __read_8(void *ptr)
{
unsigned long long data = *(unsigned long long *)ptr;
return data;
}
static unsigned long long __read_8_sw(void *ptr)
{
unsigned long long data = *(unsigned long long *)ptr;
unsigned long long swap;
swap = ((data & 0xffULL) << 56) |
((data & (0xffULL << 8)) << 40) |
((data & (0xffULL << 16)) << 24) |
((data & (0xffULL << 24)) << 8) |
((data & (0xffULL << 32)) >> 8) |
((data & (0xffULL << 40)) >> 24) |
((data & (0xffULL << 48)) >> 40) |
((data & (0xffULL << 56)) >> 56);
return swap;
}
static unsigned int __read_4(void *ptr)
{
unsigned int data = *(unsigned int *)ptr;
return data;
}
static unsigned int __read_4_sw(void *ptr)
{
unsigned int data = *(unsigned int *)ptr;
unsigned int swap;
swap = ((data & 0xffULL) << 24) |
((data & (0xffULL << 8)) << 8) |
((data & (0xffULL << 16)) >> 8) |
((data & (0xffULL << 24)) >> 24);
return swap;
}
static unsigned long long read_8(struct kbuffer *kbuf, void *ptr)
{
return kbuf->read_8(ptr);
}
static unsigned int read_4(struct kbuffer *kbuf, void *ptr)
{
return kbuf->read_4(ptr);
}
static unsigned long long __read_long_8(struct kbuffer *kbuf, void *ptr)
{
return kbuf->read_8(ptr);
}
static unsigned long long __read_long_4(struct kbuffer *kbuf, void *ptr)
{
return kbuf->read_4(ptr);
}
static unsigned long long read_long(struct kbuffer *kbuf, void *ptr)
{
return kbuf->read_long(kbuf, ptr);
}
static int calc_index(struct kbuffer *kbuf, void *ptr)
{
return (unsigned long)ptr - (unsigned long)kbuf->data;
}
static int __next_event(struct kbuffer *kbuf);
/**
* kbuffer_alloc - allocat a new kbuffer
* @size; enum to denote size of word
* @endian: enum to denote endianness
*
* Allocates and returns a new kbuffer.
*/
struct kbuffer *
kbuffer_alloc(enum kbuffer_long_size size, enum kbuffer_endian endian)
{
struct kbuffer *kbuf;
int flags = 0;
switch (size) {
case KBUFFER_LSIZE_4:
break;
case KBUFFER_LSIZE_8:
flags |= KBUFFER_FL_LONG_8;
break;
default:
return NULL;
}
switch (endian) {
case KBUFFER_ENDIAN_LITTLE:
break;
case KBUFFER_ENDIAN_BIG:
flags |= KBUFFER_FL_BIG_ENDIAN;
break;
default:
return NULL;
}
kbuf = zmalloc(sizeof(*kbuf));
if (!kbuf)
return NULL;
kbuf->flags = flags;
if (host_is_bigendian())
kbuf->flags |= KBUFFER_FL_HOST_BIG_ENDIAN;
if (do_swap(kbuf)) {
kbuf->read_8 = __read_8_sw;
kbuf->read_4 = __read_4_sw;
} else {
kbuf->read_8 = __read_8;
kbuf->read_4 = __read_4;
}
if (kbuf->flags & KBUFFER_FL_LONG_8)
kbuf->read_long = __read_long_8;
else
kbuf->read_long = __read_long_4;
/* May be changed by kbuffer_set_old_format() */
kbuf->next_event = __next_event;
return kbuf;
}
/** kbuffer_free - free an allocated kbuffer
* @kbuf: The kbuffer to free
*
* Can take NULL as a parameter.
*/
void kbuffer_free(struct kbuffer *kbuf)
{
free(kbuf);
}
static unsigned int type4host(struct kbuffer *kbuf,
unsigned int type_len_ts)
{
if (kbuf->flags & KBUFFER_FL_BIG_ENDIAN)
return (type_len_ts >> 29) & 3;
else
return type_len_ts & 3;
}
static unsigned int len4host(struct kbuffer *kbuf,
unsigned int type_len_ts)
{
if (kbuf->flags & KBUFFER_FL_BIG_ENDIAN)
return (type_len_ts >> 27) & 7;
else
return (type_len_ts >> 2) & 7;
}
static unsigned int type_len4host(struct kbuffer *kbuf,
unsigned int type_len_ts)
{
if (kbuf->flags & KBUFFER_FL_BIG_ENDIAN)
return (type_len_ts >> 27) & ((1 << 5) - 1);
else
return type_len_ts & ((1 << 5) - 1);
}
static unsigned int ts4host(struct kbuffer *kbuf,
unsigned int type_len_ts)
{
if (kbuf->flags & KBUFFER_FL_BIG_ENDIAN)
return type_len_ts & ((1 << 27) - 1);
else
return type_len_ts >> 5;
}
/*
* Linux 2.6.30 and earlier (not much ealier) had a different
* ring buffer format. It should be obsolete, but we handle it anyway.
*/
enum old_ring_buffer_type {
OLD_RINGBUF_TYPE_PADDING,
OLD_RINGBUF_TYPE_TIME_EXTEND,
OLD_RINGBUF_TYPE_TIME_STAMP,
OLD_RINGBUF_TYPE_DATA,
};
static unsigned int old_update_pointers(struct kbuffer *kbuf)
{
unsigned long long extend;
unsigned int type_len_ts;
unsigned int type;
unsigned int len;
unsigned int delta;
unsigned int length;
void *ptr = kbuf->data + kbuf->curr;
type_len_ts = read_4(kbuf, ptr);
ptr += 4;
type = type4host(kbuf, type_len_ts);
len = len4host(kbuf, type_len_ts);
delta = ts4host(kbuf, type_len_ts);
switch (type) {
case OLD_RINGBUF_TYPE_PADDING:
kbuf->next = kbuf->size;
return 0;
case OLD_RINGBUF_TYPE_TIME_EXTEND:
extend = read_4(kbuf, ptr);
extend <<= TS_SHIFT;
extend += delta;
delta = extend;
ptr += 4;
break;
case OLD_RINGBUF_TYPE_TIME_STAMP:
/* should never happen! */
kbuf->curr = kbuf->size;
kbuf->next = kbuf->size;
kbuf->index = kbuf->size;
return -1;
default:
if (len)
length = len * 4;
else {
length = read_4(kbuf, ptr);
length -= 4;
ptr += 4;
}
break;
}
kbuf->timestamp += delta;
kbuf->index = calc_index(kbuf, ptr);
kbuf->next = kbuf->index + length;
return type;
}
static int __old_next_event(struct kbuffer *kbuf)
{
int type;
do {
kbuf->curr = kbuf->next;
if (kbuf->next >= kbuf->size)
return -1;
type = old_update_pointers(kbuf);
} while (type == OLD_RINGBUF_TYPE_TIME_EXTEND || type == OLD_RINGBUF_TYPE_PADDING);
return 0;
}
static unsigned int
translate_data(struct kbuffer *kbuf, void *data, void **rptr,
unsigned long long *delta, int *length)
{
unsigned long long extend;
unsigned int type_len_ts;
unsigned int type_len;
type_len_ts = read_4(kbuf, data);
data += 4;
type_len = type_len4host(kbuf, type_len_ts);
*delta = ts4host(kbuf, type_len_ts);
switch (type_len) {
case KBUFFER_TYPE_PADDING:
*length = read_4(kbuf, data);
break;
case KBUFFER_TYPE_TIME_EXTEND:
extend = read_4(kbuf, data);
data += 4;
extend <<= TS_SHIFT;
extend += *delta;
*delta = extend;
*length = 0;
break;
case KBUFFER_TYPE_TIME_STAMP:
data += 12;
*length = 0;
break;
case 0:
*length = read_4(kbuf, data) - 4;
*length = (*length + 3) & ~3;
data += 4;
break;
default:
*length = type_len * 4;
break;
}
*rptr = data;
return type_len;
}
static unsigned int update_pointers(struct kbuffer *kbuf)
{
unsigned long long delta;
unsigned int type_len;
int length;
void *ptr = kbuf->data + kbuf->curr;
type_len = translate_data(kbuf, ptr, &ptr, &delta, &length);
kbuf->timestamp += delta;
kbuf->index = calc_index(kbuf, ptr);
kbuf->next = kbuf->index + length;
return type_len;
}
/**
* kbuffer_translate_data - read raw data to get a record
* @swap: Set to 1 if bytes in words need to be swapped when read
* @data: The raw data to read
* @size: Address to store the size of the event data.
*
* Returns a pointer to the event data. To determine the entire
* record size (record metadata + data) just add the difference between
* @data and the returned value to @size.
*/
void *kbuffer_translate_data(int swap, void *data, unsigned int *size)
{
unsigned long long delta;
struct kbuffer kbuf;
int type_len;
int length;
void *ptr;
if (swap) {
kbuf.read_8 = __read_8_sw;
kbuf.read_4 = __read_4_sw;
kbuf.flags = host_is_bigendian() ? 0 : KBUFFER_FL_BIG_ENDIAN;
} else {
kbuf.read_8 = __read_8;
kbuf.read_4 = __read_4;
kbuf.flags = host_is_bigendian() ? KBUFFER_FL_BIG_ENDIAN: 0;
}
type_len = translate_data(&kbuf, data, &ptr, &delta, &length);
switch (type_len) {
case KBUFFER_TYPE_PADDING:
case KBUFFER_TYPE_TIME_EXTEND:
case KBUFFER_TYPE_TIME_STAMP:
return NULL;
};
*size = length;
return ptr;
}
static int __next_event(struct kbuffer *kbuf)
{
int type;
do {
kbuf->curr = kbuf->next;
if (kbuf->next >= kbuf->size)
return -1;
type = update_pointers(kbuf);
} while (type == KBUFFER_TYPE_TIME_EXTEND || type == KBUFFER_TYPE_PADDING);
return 0;
}
static int next_event(struct kbuffer *kbuf)
{
return kbuf->next_event(kbuf);
}
/**
* kbuffer_next_event - increment the current pointer
* @kbuf: The kbuffer to read
* @ts: Address to store the next record's timestamp (may be NULL to ignore)
*
* Increments the pointers into the subbuffer of the kbuffer to point to the
* next event so that the next kbuffer_read_event() will return a
* new event.
*
* Returns the data of the next event if a new event exists on the subbuffer,
* NULL otherwise.
*/
void *kbuffer_next_event(struct kbuffer *kbuf, unsigned long long *ts)
{
int ret;
if (!kbuf || !kbuf->subbuffer)
return NULL;
ret = next_event(kbuf);
if (ret < 0)
return NULL;
if (ts)
*ts = kbuf->timestamp;
return kbuf->data + kbuf->index;
}
/**
* kbuffer_load_subbuffer - load a new subbuffer into the kbuffer
* @kbuf: The kbuffer to load
* @subbuffer: The subbuffer to load into @kbuf.
*
* Load a new subbuffer (page) into @kbuf. This will reset all
* the pointers and update the @kbuf timestamp. The next read will
* return the first event on @subbuffer.
*
* Returns 0 on succes, -1 otherwise.
*/
int kbuffer_load_subbuffer(struct kbuffer *kbuf, void *subbuffer)
{
unsigned long long flags;
void *ptr = subbuffer;
if (!kbuf || !subbuffer)
return -1;
kbuf->subbuffer = subbuffer;
kbuf->timestamp = read_8(kbuf, ptr);
ptr += 8;
kbuf->curr = 0;
if (kbuf->flags & KBUFFER_FL_LONG_8)
kbuf->start = 16;
else
kbuf->start = 12;
kbuf->data = subbuffer + kbuf->start;
flags = read_long(kbuf, ptr);
kbuf->size = (unsigned int)flags & COMMIT_MASK;
if (flags & MISSING_EVENTS) {
if (flags & MISSING_STORED) {
ptr = kbuf->data + kbuf->size;
kbuf->lost_events = read_long(kbuf, ptr);
} else
kbuf->lost_events = -1;
} else
kbuf->lost_events = 0;
kbuf->index = 0;
kbuf->next = 0;
next_event(kbuf);
return 0;
}
/**
* kbuffer_read_event - read the next event in the kbuffer subbuffer
* @kbuf: The kbuffer to read from
* @ts: The address to store the timestamp of the event (may be NULL to ignore)
*
* Returns a pointer to the data part of the current event.
* NULL if no event is left on the subbuffer.
*/
void *kbuffer_read_event(struct kbuffer *kbuf, unsigned long long *ts)
{
if (!kbuf || !kbuf->subbuffer)
return NULL;
if (kbuf->curr >= kbuf->size)
return NULL;
if (ts)
*ts = kbuf->timestamp;
return kbuf->data + kbuf->index;
}
/**
* kbuffer_timestamp - Return the timestamp of the current event
* @kbuf: The kbuffer to read from
*
* Returns the timestamp of the current (next) event.
*/
unsigned long long kbuffer_timestamp(struct kbuffer *kbuf)
{
return kbuf->timestamp;
}
/**
* kbuffer_read_at_offset - read the event that is at offset
* @kbuf: The kbuffer to read from
* @offset: The offset into the subbuffer
* @ts: The address to store the timestamp of the event (may be NULL to ignore)
*
* The @offset must be an index from the @kbuf subbuffer beginning.
* If @offset is bigger than the stored subbuffer, NULL will be returned.
*
* Returns the data of the record that is at @offset. Note, @offset does
* not need to be the start of the record, the offset just needs to be
* in the record (or beginning of it).
*
* Note, the kbuf timestamp and pointers are updated to the
* returned record. That is, kbuffer_read_event() will return the same
* data and timestamp, and kbuffer_next_event() will increment from
* this record.
*/
void *kbuffer_read_at_offset(struct kbuffer *kbuf, int offset,
unsigned long long *ts)
{
void *data;
if (offset < kbuf->start)
offset = 0;
else
offset -= kbuf->start;
/* Reset the buffer */
kbuffer_load_subbuffer(kbuf, kbuf->subbuffer);
while (kbuf->curr < offset) {
data = kbuffer_next_event(kbuf, ts);
if (!data)
break;
}
return data;
}
/**
* kbuffer_subbuffer_size - the size of the loaded subbuffer
* @kbuf: The kbuffer to read from
*
* Returns the size of the subbuffer. Note, this size is
* where the last event resides. The stored subbuffer may actually be
* bigger due to padding and such.
*/
int kbuffer_subbuffer_size(struct kbuffer *kbuf)
{
return kbuf->size;
}
/**
* kbuffer_curr_index - Return the index of the record
* @kbuf: The kbuffer to read from
*
* Returns the index from the start of the data part of
* the subbuffer to the current location. Note this is not
* from the start of the subbuffer. An index of zero will
* point to the first record. Use kbuffer_curr_offset() for
* the actually offset (that can be used by kbuffer_read_at_offset())
*/
int kbuffer_curr_index(struct kbuffer *kbuf)
{
return kbuf->curr;
}
/**
* kbuffer_curr_offset - Return the offset of the record
* @kbuf: The kbuffer to read from
*
* Returns the offset from the start of the subbuffer to the
* current location.
*/
int kbuffer_curr_offset(struct kbuffer *kbuf)
{
return kbuf->curr + kbuf->start;
}
/**
* kbuffer_event_size - return the size of the event data
* @kbuf: The kbuffer to read
*
* Returns the size of the event data (the payload not counting
* the meta data of the record) of the current event.
*/
int kbuffer_event_size(struct kbuffer *kbuf)
{
return kbuf->next - kbuf->index;
}
/**
* kbuffer_curr_size - return the size of the entire record
* @kbuf: The kbuffer to read
*
* Returns the size of the entire record (meta data and payload)
* of the current event.
*/
int kbuffer_curr_size(struct kbuffer *kbuf)
{
return kbuf->next - kbuf->curr;
}
/**
* kbuffer_missed_events - return the # of missed events from last event.
* @kbuf: The kbuffer to read from
*
* Returns the # of missed events (if recorded) before the current
* event. Note, only events on the beginning of a subbuffer can
* have missed events, all other events within the buffer will be
* zero.
*/
int kbuffer_missed_events(struct kbuffer *kbuf)
{
/* Only the first event can have missed events */
if (kbuf->curr)
return 0;
return kbuf->lost_events;
}
/**
* kbuffer_set_old_forma - set the kbuffer to use the old format parsing
* @kbuf: The kbuffer to set
*
* This is obsolete (or should be). The first kernels to use the
* new ring buffer had a slightly different ring buffer format
* (2.6.30 and earlier). It is still somewhat supported by kbuffer,
* but should not be counted on in the future.
*/
void kbuffer_set_old_format(struct kbuffer *kbuf)
{
kbuf->flags |= KBUFFER_FL_OLD_FORMAT;
kbuf->next_event = __old_next_event;
}

View file

@ -0,0 +1,67 @@
/*
* Copyright (C) 2012 Red Hat Inc, Steven Rostedt <srostedt@redhat.com>
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License (not later!)
*
* This program 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
#ifndef _KBUFFER_H
#define _KBUFFER_H
#ifndef TS_SHIFT
#define TS_SHIFT 27
#endif
enum kbuffer_endian {
KBUFFER_ENDIAN_BIG,
KBUFFER_ENDIAN_LITTLE,
};
enum kbuffer_long_size {
KBUFFER_LSIZE_4,
KBUFFER_LSIZE_8,
};
enum {
KBUFFER_TYPE_PADDING = 29,
KBUFFER_TYPE_TIME_EXTEND = 30,
KBUFFER_TYPE_TIME_STAMP = 31,
};
struct kbuffer;
struct kbuffer *kbuffer_alloc(enum kbuffer_long_size size, enum kbuffer_endian endian);
void kbuffer_free(struct kbuffer *kbuf);
int kbuffer_load_subbuffer(struct kbuffer *kbuf, void *subbuffer);
void *kbuffer_read_event(struct kbuffer *kbuf, unsigned long long *ts);
void *kbuffer_next_event(struct kbuffer *kbuf, unsigned long long *ts);
unsigned long long kbuffer_timestamp(struct kbuffer *kbuf);
void *kbuffer_translate_data(int swap, void *data, unsigned int *size);
void *kbuffer_read_at_offset(struct kbuffer *kbuf, int offset, unsigned long long *ts);
int kbuffer_curr_index(struct kbuffer *kbuf);
int kbuffer_curr_offset(struct kbuffer *kbuf);
int kbuffer_curr_size(struct kbuffer *kbuf);
int kbuffer_event_size(struct kbuffer *kbuf);
int kbuffer_missed_events(struct kbuffer *kbuf);
int kbuffer_subbuffer_size(struct kbuffer *kbuf);
void kbuffer_set_old_format(struct kbuffer *kbuf);
#endif /* _K_BUFFER_H */

File diff suppressed because it is too large Load diff

Some files were not shown because too many files have changed in this diff Show more